Skip to content

Commit 05e4af8

Browse files
author
Brent Cook
committed
Land rapid7#5214, initial meterpreter session recovery support
2 parents d90c25e + 3771a78 commit 05e4af8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1689
-385
lines changed

Gemfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ PATH
99
json
1010
metasploit-concern (= 0.4.0)
1111
metasploit-model (~> 0.29.0)
12-
meterpreter_bins (= 0.0.22)
12+
metasploit-payloads (= 0.0.3)
1313
msgpack
1414
nokogiri
1515
packetfu (= 1.1.9)
@@ -123,6 +123,7 @@ GEM
123123
metasploit-model (0.29.2)
124124
activesupport
125125
railties (< 4.0.0)
126+
metasploit-payloads (0.0.3)
126127
metasploit_data_models (0.24.0)
127128
activerecord (>= 3.2.13, < 4.0.0)
128129
activesupport
@@ -132,7 +133,6 @@ GEM
132133
pg
133134
railties (< 4.0.0)
134135
recog (~> 1.0)
135-
meterpreter_bins (0.0.22)
136136
method_source (0.8.2)
137137
mime-types (1.25.1)
138138
mini_portile (0.6.2)
-35.3 KB
Binary file not shown.
-42.6 KB
Binary file not shown.
-204 KB
Binary file not shown.
-1.42 MB
Binary file not shown.

lib/msf/base/sessions/meterpreter_options.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ def initialize(info = {})
1616
OptString.new('AutoRunScript', [false, "A script to run automatically on session creation.", '']),
1717
OptBool.new('AutoSystemInfo', [true, "Automatically capture system information on initialization.", true]),
1818
OptBool.new('EnableUnicodeEncoding', [true, "Automatically encode UTF-8 strings as hexadecimal", Rex::Compat.is_windows]),
19-
OptPath.new('HandlerSSLCert', [false, "Path to a SSL certificate in unified PEM format, ignored for HTTP transports"])
19+
OptPath.new('HandlerSSLCert', [false, "Path to a SSL certificate in unified PEM format, ignored for HTTP transports"]),
20+
OptBool.new('StagerCloseListenSocket', [false, "Close the listen socket in the stager", false]),
21+
OptInt.new('SessionRetryTotal', [false, "Number of seconds try reconnecting for on network failure", Rex::Post::Meterpreter::ClientCore::TIMEOUT_RETRY_TOTAL]),
22+
OptInt.new('SessionRetryWait', [false, "Number of seconds to wait between reconnect attempts", Rex::Post::Meterpreter::ClientCore::TIMEOUT_RETRY_WAIT]),
23+
OptInt.new('SessionExpirationTimeout', [ false, 'The number of seconds before this session should be forcibly shut down', Rex::Post::Meterpreter::ClientCore::TIMEOUT_SESSION]),
24+
OptInt.new('SessionCommunicationTimeout', [ false, 'The number of seconds of no activity before this session should be killed', Rex::Post::Meterpreter::ClientCore::TIMEOUT_COMMS])
2025
], self.class)
2126
end
2227

lib/msf/core/handler/bind_tcp.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,12 +141,21 @@ def start_handler
141141
# Increment the has connection counter
142142
self.pending_connections += 1
143143

144+
# Timeout and datastore options need to be passed through to the client
145+
opts = {
146+
:datastore => datastore,
147+
:expiration => datastore['SessionExpirationTimeout'].to_i,
148+
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
149+
:retry_total => datastore['SessionRetryTotal'].to_i,
150+
:retry_wait => datastore['SessionRetryWait'].to_i
151+
}
152+
144153
# Start a new thread and pass the client connection
145154
# as the input and output pipe. Client's are expected
146155
# to implement the Stream interface.
147156
conn_threads << framework.threads.spawn("BindTcpHandlerSession", false, client) { |client_copy|
148157
begin
149-
handle_connection(wrap_aes_socket(client_copy), { datastore: datastore })
158+
handle_connection(wrap_aes_socket(client_copy), opts)
150159
rescue
151160
elog("Exception raised from BindTcp.handle_connection: #{$!}")
152161
end

lib/msf/core/handler/reverse_hop_http.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ def send_new_stage
250250
#
251251
# Patch options into the payload
252252
#
253-
Rex::Payloads::Meterpreter::Patch.patch_passive_service! blob,
253+
Rex::Payloads::Meterpreter::Patch.patch_passive_service!(blob,
254254
:ssl => ssl?,
255255
:url => url,
256256
:expiration => datastore['SessionExpirationTimeout'],
@@ -260,7 +260,7 @@ def send_new_stage
260260
:proxy_port => datastore['PayloadProxyPort'],
261261
:proxy_type => datastore['PayloadProxyType'],
262262
:proxy_user => datastore['PayloadProxyUser'],
263-
:proxy_pass => datastore['PayloadProxyPass']
263+
:proxy_pass => datastore['PayloadProxyPass'])
264264

265265
blob = encode_stage(blob)
266266

lib/msf/core/handler/reverse_http.rb

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,6 @@ def initialize(info = {})
5252
register_advanced_options(
5353
[
5454
OptString.new('ReverseListenerComm', [ false, 'The specific communication channel to use for this listener']),
55-
OptInt.new('SessionExpirationTimeout', [ false, 'The number of seconds before this session should be forcibly shut down', (24*3600*7)]),
56-
OptInt.new('SessionCommunicationTimeout', [ false, 'The number of seconds of no activity before this session should be killed', 300]),
5755
OptString.new('MeterpreterUserAgent', [ false, 'The user-agent that the payload should use for communication', 'Mozilla/4.0 (compatible; MSIE 6.1; Windows NT)' ]),
5856
OptString.new('MeterpreterServerName', [ false, 'The server header that the handler will send in response to requests', 'Apache' ]),
5957
OptAddress.new('ReverseListenerBindAddress', [ false, 'The specific IP address to bind to on the local system']),
@@ -283,6 +281,8 @@ def on_request(cli, req, obj)
283281
:url => url,
284282
:expiration => datastore['SessionExpirationTimeout'].to_i,
285283
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
284+
:retry_total => datastore['SessionRetryTotal'].to_i,
285+
:retry_wait => datastore['SessionRetryWait'].to_i,
286286
:ssl => ssl?,
287287
:payload_uuid => uuid
288288
})
@@ -312,6 +312,8 @@ def on_request(cli, req, obj)
312312
:url => url,
313313
:expiration => datastore['SessionExpirationTimeout'].to_i,
314314
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
315+
:retry_total => datastore['SessionRetryTotal'].to_i,
316+
:retry_wait => datastore['SessionRetryWait'].to_i,
315317
:ssl => ssl?,
316318
:payload_uuid => uuid
317319
})
@@ -330,17 +332,19 @@ def on_request(cli, req, obj)
330332
# Patch options into the payload
331333
#
332334
Rex::Payloads::Meterpreter::Patch.patch_passive_service!(blob,
333-
:ssl => ssl?,
334-
:url => url,
335-
:ssl_cert_hash => verify_cert_hash,
336-
:expiration => datastore['SessionExpirationTimeout'],
337-
:comm_timeout => datastore['SessionCommunicationTimeout'],
338-
:ua => datastore['MeterpreterUserAgent'],
339-
:proxy_host => datastore['PayloadProxyHost'],
340-
:proxy_port => datastore['PayloadProxyPort'],
341-
:proxy_type => datastore['PayloadProxyType'],
342-
:proxy_user => datastore['PayloadProxyUser'],
343-
:proxy_pass => datastore['PayloadProxyPass'])
335+
:ssl => ssl?,
336+
:url => url,
337+
:ssl_cert_hash => verify_cert_hash,
338+
:expiration => datastore['SessionExpirationTimeout'].to_i,
339+
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
340+
:retry_total => datastore['SessionRetryTotal'].to_i,
341+
:retry_wait => datastore['SessionRetryWait'].to_i,
342+
:ua => datastore['MeterpreterUserAgent'],
343+
:proxy_host => datastore['PayloadProxyHost'],
344+
:proxy_port => datastore['PayloadProxyPort'],
345+
:proxy_type => datastore['PayloadProxyType'],
346+
:proxy_user => datastore['PayloadProxyUser'],
347+
:proxy_pass => datastore['PayloadProxyPass'])
344348

345349
resp.body = encode_stage(blob)
346350

@@ -351,6 +355,8 @@ def on_request(cli, req, obj)
351355
:url => url,
352356
:expiration => datastore['SessionExpirationTimeout'].to_i,
353357
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
358+
:retry_total => datastore['SessionRetryTotal'].to_i,
359+
:retry_wait => datastore['SessionRetryWait'].to_i,
354360
:ssl => ssl?,
355361
:payload_uuid => uuid
356362
})
@@ -366,8 +372,13 @@ def on_request(cli, req, obj)
366372
:passive_dispatcher => obj.service,
367373
:conn_id => conn_id,
368374
:url => payload_uri(req) + conn_id + "/\x00",
369-
:expiration => datastore['SessionExpirationTimeout'].to_i,
370-
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
375+
# TODO ### Figure out what to do with these options given that the payload ###
376+
# settings might not match the handler, should we instead read the remote? #
377+
:expiration => datastore['SessionExpirationTimeout'].to_i, #
378+
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i, #
379+
:retry_total => datastore['SessionRetryTotal'].to_i, #
380+
:retry_wait => datastore['SessionRetryWait'].to_i, #
381+
##############################################################################
371382
:ssl => ssl?,
372383
:payload_uuid => uuid
373384
})

lib/msf/core/handler/reverse_http/stageless.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ def generate_stageless(opts={})
5858
:ssl_cert_hash => verify_cert_hash,
5959
:expiration => datastore['SessionExpirationTimeout'].to_i,
6060
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
61+
:retry_total => datastore['SessionRetryTotal'].to_i,
62+
:retry_wait => datastore['SessionRetryWait'].to_i,
6163
:ua => datastore['MeterpreterUserAgent'],
6264
:proxy_host => datastore['PayloadProxyHost'],
6365
:proxy_port => datastore['PayloadProxyPort'],

0 commit comments

Comments
 (0)