Skip to content

Commit 9de4137

Browse files
author
HD Moore
committed
Patch UA/Proxy settings during migration, lands rapid7#3632
2 parents f6af86a + 370f600 commit 9de4137

File tree

6 files changed

+188
-133
lines changed

6 files changed

+188
-133
lines changed

lib/msf/core/handler/reverse_hop_http.rb

Lines changed: 15 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def start_handler
8484
return
8585
end
8686

87-
# Sometimes you just have to do everything yourself.
87+
# Sometimes you just have to do everything yourself.
8888
# Declare ownership of this hop and spawn a thread to monitor it.
8989
self.refs = 1
9090
ReverseHopHttp.hop_handlers[full_uri] = self
@@ -247,40 +247,20 @@ def send_new_stage
247247

248248
print_status("Preparing stage for next session #{conn_id}")
249249
blob = stage_payload
250-
251-
# Replace the user agent string with our option
252-
i = blob.index("METERPRETER_UA\x00")
253-
if i
254-
str = datastore['MeterpreterUserAgent'][0,255] + "\x00"
255-
blob[i, str.length] = str
256-
end
257-
258-
# Replace the transport string first (TRANSPORT_SOCKET_SSL)
259-
i = blob.index("METERPRETER_TRANSPORT_SSL")
260-
if i
261-
str = "METERPRETER_TRANSPORT_HTTP#{ssl? ? "S" : ""}\x00"
262-
blob[i, str.length] = str
263-
end
264-
265-
conn_id = generate_uri_checksum(URI_CHECKSUM_CONN) + "_" + Rex::Text.rand_text_alphanumeric(16)
266-
i = blob.index("https://" + ("X" * 256))
267-
if i
268-
url = full_uri + conn_id + "/\x00"
269-
blob[i, url.length] = url
270-
end
271-
print_status("Patched URL at offset #{i}...")
272-
273-
i = blob.index([0xb64be661].pack("V"))
274-
if i
275-
str = [ datastore['SessionExpirationTimeout'] ].pack("V")
276-
blob[i, str.length] = str
277-
end
278-
279-
i = blob.index([0xaf79257f].pack("V"))
280-
if i
281-
str = [ datastore['SessionCommunicationTimeout'] ].pack("V")
282-
blob[i, str.length] = str
283-
end
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+
:proxyhost => datastore['PROXYHOST'],
260+
:proxyport => datastore['PROXYPORT'],
261+
:proxy_type => datastore['PROXY_TYPE'],
262+
:proxy_username => datastore['PROXY_USERNAME'],
263+
:proxy_password => datastore['PROXY_PASSWORD']
284264

285265
blob = encode_stage(blob)
286266

lib/msf/core/handler/reverse_http.rb

Lines changed: 17 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
require 'rex/io/stream_abstraction'
33
require 'rex/sync/ref'
44
require 'msf/core/handler/reverse_http/uri_checksum'
5+
require 'rex/payloads/meterpreter/patch'
56

67
module Msf
78
module Handler
@@ -257,86 +258,28 @@ def on_request(cli, req, obj)
257258
})
258259

259260
when /^\/A?INITM?/
260-
url = ''
261+
conn_id = generate_uri_checksum(URI_CHECKSUM_CONN) + "_" + Rex::Text.rand_text_alphanumeric(16)
262+
url = payload_uri + conn_id + "/\x00"
261263

262264
print_status("#{cli.peerhost}:#{cli.peerport} Staging connection for target #{req.relative_resource} received...")
263265
resp['Content-Type'] = 'application/octet-stream'
264266

265267
blob = obj.stage_payload
266268

267-
# Replace the user agent string with our option
268-
i = blob.index("METERPRETER_UA\x00")
269-
if i
270-
str = datastore['MeterpreterUserAgent'][0,255] + "\x00"
271-
blob[i, str.length] = str
272-
print_status("Patched user-agent at offset #{i}...")
273-
end
274-
275-
# Activate a custom proxy
276-
i = blob.index("METERPRETER_PROXY\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
277-
if i
278-
if datastore['PROXYHOST']
279-
if datastore['PROXYHOST'].to_s != ""
280-
proxyhost = datastore['PROXYHOST'].to_s
281-
proxyport = datastore['PROXYPORT'].to_s || "8080"
282-
proxyinfo = proxyhost + ":" + proxyport
283-
if proxyport == "80"
284-
proxyinfo = proxyhost
285-
end
286-
if datastore['PROXY_TYPE'].to_s == 'HTTP'
287-
proxyinfo = 'http://' + proxyinfo
288-
else #socks
289-
proxyinfo = 'socks=' + proxyinfo
290-
end
291-
proxyinfo << "\x00"
292-
blob[i, proxyinfo.length] = proxyinfo
293-
print_status("Activated custom proxy #{proxyinfo}, patch at offset #{i}...")
294-
#Optional authentification
295-
unless (datastore['PROXY_USERNAME'].nil? or datastore['PROXY_USERNAME'].empty?) or
296-
(datastore['PROXY_PASSWORD'].nil? or datastore['PROXY_PASSWORD'].empty?) or
297-
datastore['PROXY_TYPE'] == 'SOCKS'
298-
299-
proxy_username_loc = blob.index("METERPRETER_USERNAME_PROXY\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
300-
proxy_username = datastore['PROXY_USERNAME'] << "\x00"
301-
blob[proxy_username_loc, proxy_username.length] = proxy_username
302-
303-
proxy_password_loc = blob.index("METERPRETER_PASSWORD_PROXY\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
304-
proxy_password = datastore['PROXY_PASSWORD'] << "\x00"
305-
blob[proxy_password_loc, proxy_password.length] = proxy_password
306-
end
307-
end
308-
end
309-
end
310-
311-
# Replace the transport string first (TRANSPORT_SOCKET_SSL)
312-
i = blob.index("METERPRETER_TRANSPORT_SSL")
313-
if i
314-
str = "METERPRETER_TRANSPORT_HTTP#{ssl? ? "S" : ""}\x00"
315-
blob[i, str.length] = str
316-
end
317-
print_status("Patched transport at offset #{i}...")
318-
319-
conn_id = generate_uri_checksum(URI_CHECKSUM_CONN) + "_" + Rex::Text.rand_text_alphanumeric(16)
320-
i = blob.index("https://" + ("X" * 256))
321-
if i
322-
url = payload_uri + conn_id + "/\x00"
323-
blob[i, url.length] = url
324-
end
325-
print_status("Patched URL at offset #{i}...")
326-
327-
i = blob.index([0xb64be661].pack("V"))
328-
if i
329-
str = [ datastore['SessionExpirationTimeout'] ].pack("V")
330-
blob[i, str.length] = str
331-
end
332-
print_status("Patched Expiration Timeout at offset #{i}...")
333-
334-
i = blob.index([0xaf79257f].pack("V"))
335-
if i
336-
str = [ datastore['SessionCommunicationTimeout'] ].pack("V")
337-
blob[i, str.length] = str
338-
end
339-
print_status("Patched Communication Timeout at offset #{i}...")
269+
#
270+
# Patch options into the payload
271+
#
272+
Rex::Payloads::Meterpreter::Patch.patch_passive_service! blob,
273+
:ssl => ssl?,
274+
:url => url,
275+
:expiration => datastore['SessionExpirationTimeout'],
276+
:comm_timeout => datastore['SessionCommunicationTimeout'],
277+
:ua => datastore['MeterpreterUserAgent'],
278+
:proxyhost => datastore['PROXYHOST'],
279+
:proxyport => datastore['PROXYPORT'],
280+
:proxy_type => datastore['PROXY_TYPE'],
281+
:proxy_username => datastore['PROXY_USERNAME'],
282+
:proxy_password => datastore['PROXY_PASSWORD']
340283

341284
resp.body = encode_stage(blob)
342285

lib/rex/payloads.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
# -*- coding: binary -*-
22
require 'rex/payloads/win32'
3+
require 'rex/payloads/meterpreter'

lib/rex/payloads/meterpreter.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# -*- coding: binary -*-
2+
require 'rex/payloads/meterpreter/patch'

lib/rex/payloads/meterpreter/patch.rb

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# -*- coding: binary -*-
2+
3+
module Rex
4+
module Payloads
5+
module Meterpreter
6+
###
7+
#
8+
# Provides methods to patch options into metsrv stagers
9+
#
10+
###
11+
module Patch
12+
13+
# Replace the transport string
14+
def self.patch_transport! blob, ssl
15+
16+
i = blob.index("METERPRETER_TRANSPORT_SSL")
17+
if i
18+
str = ssl ? "METERPRETER_TRANSPORT_HTTPS\x00" : "METERPRETER_TRANSPORT_HTTP\x00"
19+
blob[i, str.length] = str
20+
end
21+
22+
end
23+
24+
# Replace the URL
25+
def self.patch_url! blob, url
26+
27+
i = blob.index("https://" + ("X" * 256))
28+
if i
29+
str = url
30+
blob[i, str.length] = str
31+
end
32+
33+
end
34+
35+
# Replace the session expiration timeout
36+
def self.patch_expiration! blob, expiration
37+
38+
i = blob.index([0xb64be661].pack("V"))
39+
if i
40+
str = [ expiration ].pack("V")
41+
blob[i, str.length] = str
42+
end
43+
44+
end
45+
46+
# Replace the session communication timeout
47+
def self.patch_comm_timeout! blob, comm_timeout
48+
49+
i = blob.index([0xaf79257f].pack("V"))
50+
if i
51+
str = [ comm_timeout ].pack("V")
52+
blob[i, str.length] = str
53+
end
54+
55+
end
56+
57+
# Replace the user agent string with our option
58+
def self.patch_ua! blob, ua
59+
60+
ua = ua[0,255] + "\x00"
61+
i = blob.index("METERPRETER_UA\x00")
62+
if i
63+
blob[i, ua.length] = ua
64+
end
65+
66+
end
67+
68+
# Activate a custom proxy
69+
def self.patch_proxy! blob, proxyhost, proxyport, proxy_type
70+
71+
i = blob.index("METERPRETER_PROXY\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
72+
if i
73+
if proxyhost
74+
if proxyhost.to_s != ""
75+
proxyhost = proxyhost.to_s
76+
proxyport = proxyport.to_s || "8080"
77+
proxyinfo = proxyhost + ":" + proxyport
78+
if proxyport == "80"
79+
proxyinfo = proxyhost
80+
end
81+
if proxy_type.to_s == 'HTTP'
82+
proxyinfo = 'http://' + proxyinfo
83+
else #socks
84+
proxyinfo = 'socks=' + proxyinfo
85+
end
86+
proxyinfo << "\x00"
87+
blob[i, proxyinfo.length] = proxyinfo
88+
end
89+
end
90+
end
91+
92+
end
93+
94+
# Proxy authentification
95+
def self.patch_proxy_auth! blob, proxy_username, proxy_password, proxy_type
96+
97+
unless (proxy_username.nil? or proxy_username.empty?) or
98+
(proxy_password.nil? or proxy_password.empty?) or
99+
proxy_type == 'SOCKS'
100+
101+
proxy_username_loc = blob.index("METERPRETER_USERNAME_PROXY\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
102+
proxy_username = proxy_username << "\x00"
103+
blob[proxy_username_loc, proxy_username.length] = proxy_username
104+
105+
proxy_password_loc = blob.index("METERPRETER_PASSWORD_PROXY\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
106+
proxy_password = proxy_password << "\x00"
107+
blob[proxy_password_loc, proxy_password.length] = proxy_password
108+
end
109+
110+
end
111+
112+
# Patch options into metsrv for reverse HTTP payloads
113+
def self.patch_passive_service! blob, options
114+
115+
patch_transport! blob, options[:ssl]
116+
patch_url! blob, options[:url]
117+
patch_expiration! blob, options[:expiration]
118+
patch_comm_timeout! blob, options[:comm_timeout]
119+
patch_ua! blob, options[:ua]
120+
patch_proxy!(blob,
121+
options[:proxyhost],
122+
options[:proxyport],
123+
options[:proxy_type]
124+
)
125+
patch_proxy_auth!(blob,
126+
options[:proxy_username],
127+
options[:proxy_password],
128+
options[:proxy_type]
129+
)
130+
131+
end
132+
133+
end
134+
end
135+
end
136+
end

lib/rex/post/meterpreter/client_core.rb

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
# argument for moving the meterpreter client into the Msf namespace.
99
require 'msf/core/payload/windows'
1010

11+
# Provides methods to patch options into the metsrv stager.
12+
require 'rex/payloads/meterpreter/patch'
13+
1114
module Rex
1215
module Post
1316
module Meterpreter
@@ -228,31 +231,21 @@ def migrate( pid )
228231

229232
if client.passive_service
230233

231-
# Replace the transport string first (TRANSPORT_SOCKET_SSL
232-
i = blob.index("METERPRETER_TRANSPORT_SSL")
233-
if i
234-
str = client.ssl ? "METERPRETER_TRANSPORT_HTTPS\x00" : "METERPRETER_TRANSPORT_HTTP\x00"
235-
blob[i, str.length] = str
236-
end
237-
238-
conn_id = self.client.conn_id
239-
i = blob.index("https://" + ("X" * 256))
240-
if i
241-
str = self.client.url
242-
blob[i, str.length] = str
243-
end
234+
#
235+
# Patch options into metsrv for reverse HTTP payloads
236+
#
237+
Rex::Payloads::Meterpreter::Patch.patch_passive_service! blob,
238+
:ssl => client.ssl,
239+
:url => self.client.url,
240+
:expiration => self.client.expiration,
241+
:comm_timeout => self.client.comm_timeout,
242+
:ua => client.exploit_datastore['MeterpreterUserAgent'],
243+
:proxyhost => client.exploit_datastore['PROXYHOST'],
244+
:proxyport => client.exploit_datastore['PROXYPORT'],
245+
:proxy_type => client.exploit_datastore['PROXY_TYPE'],
246+
:proxy_username => client.exploit_datastore['PROXY_USERNAME'],
247+
:proxy_password => client.exploit_datastore['PROXY_PASSWORD']
244248

245-
i = blob.index([0xb64be661].pack("V"))
246-
if i
247-
str = [ self.client.expiration ].pack("V")
248-
blob[i, str.length] = str
249-
end
250-
251-
i = blob.index([0xaf79257f].pack("V"))
252-
if i
253-
str = [ self.client.comm_timeout ].pack("V")
254-
blob[i, str.length] = str
255-
end
256249
end
257250

258251
# Build the migration request

0 commit comments

Comments
 (0)