2005-09-22 03:24:32 +00:00
require 'rex/service_manager'
module Msf
###
#
# This module provides methods for acting as an HTTP client when
# exploiting an HTTP server.
#
###
module Exploit::Remote::HttpClient
2005-11-15 15:11:43 +00:00
#
# Initializes an exploit module that exploits a vulnerability in an HTTP
# server.
#
2005-09-22 03:24:32 +00:00
def initialize ( info = { } )
super
register_options (
[
Opt :: RHOST ,
2005-12-14 00:21:23 +00:00
Opt :: RPORT ( 80 ) ,
2006-01-27 22:02:35 +00:00
OptString . new ( 'VHOST' , [ false , " HTTP server virtual host " ] ) ,
Opt :: SSL ,
] , Exploit :: Remote :: HttpClient
)
register_evasion_options (
[
2006-02-06 22:48:10 +00:00
OptEnum . new ( 'HTTP::uri_encode' , [ false , 'Enable URI encoding' , 'none' , [ 'none' , 'hex-normal' , 'hex-all' , 'u-normal' , 'u-all' ] , 'none' ] ) ,
2006-01-27 22:02:35 +00:00
OptBool . new ( 'HTTP::chunked' , [ false , 'Enable chunking of HTTP request via "Transfer-Encoding: chunked"' , 'false' ] ) ,
OptBool . new ( 'HTTP::header_folding' , [ false , 'Enable folding of HTTP headers' , 'false' ] ) ,
OptBool . new ( 'HTTP::junk_headers' , [ false , 'Enable insertion of random junk HTTP headers' , 'false' ] ) ,
OptBool . new ( 'HTTP::junk_slashes' , [ false , 'Enable insertion of random junk HTTP headers' , 'false' ] ) ,
OptBool . new ( 'HTTP::junk_directories' , [ false , 'Enable insertion of random junk directories in the URI' , 'false' ] ) ,
OptBool . new ( 'HTTP::junk_params' , [ false , 'Enable insertion of random junk parameters' , 'false' ] ) ,
OptBool . new ( 'HTTP::junk_self_referring_directories' , [ false , 'Enable insertion of random self referring directories (eg: /./././)' , 'false' ] ) ,
OptInt . new ( 'HTTP::junk_pipeline' , [ true , 'Insert the specified number of junk pipeline requests' , 0 ] ) ,
2006-02-27 19:59:41 +00:00
OptBool . new ( 'HTTP::fake_uri_end' , [ false , 'Add a fake end of URI (eg: /%20HTTP/1.0/../../)' , 'false' ] ) ,
OptBool . new ( 'HTTP::fake_param_start' , [ false , 'Add a fake start of params to the URI (eg: /%3fa=b/../)' , 'false' ] ) ,
2006-01-27 22:02:35 +00:00
] , Exploit :: Remote :: HttpClient
)
2005-09-22 03:24:32 +00:00
end
#
# Connects to an HTTP server.
#
2005-12-14 00:21:23 +00:00
def connect ( opts = { } )
2005-09-22 03:24:32 +00:00
nclient = Rex :: Proto :: Http :: Client . new (
datastore [ 'RHOST' ] ,
2006-01-17 04:35:44 +00:00
datastore [ 'RPORT' ] . to_i ,
{
'Msf' = > framework ,
'MsfExploit' = > self ,
2006-01-27 22:02:35 +00:00
} ,
datastore [ 'SSL' ]
2006-01-17 04:35:44 +00:00
)
2005-09-22 03:24:32 +00:00
# Configure the HTTP client with the supplied parameter
2006-01-27 22:02:35 +00:00
nclient . config ( { 'vhost' = > datastore [ 'VHOST' ] , } . update ( opts ) )
2005-09-22 03:24:32 +00:00
# If this connection is global, persist it
if ( opts [ 'global' ] )
if ( self . client )
disconnect
end
self . client = nclient
end
return nclient
end
#
# Passes the client connection down to the handler to see if it's of any
# use.
#
def handler ( nsock = nil )
# If no socket was provided, try the global one.
if ( ( ! nsock ) and
( self . client ) )
nsock = self . client . conn
end
# If the parent claims the socket associated with the HTTP client, then
# we rip the socket out from under the HTTP client.
if ( ( ( rv = super ( nsock ) ) == Handler :: Claimed ) and
( self . client ) and
( nsock == self . client . conn ) )
self . client . conn = nil
end
rv
end
#
# Disconnects the HTTP client
#
def disconnect ( nclient = self . client )
if ( nclient )
nclient . close
end
if ( nclient == self . client )
self . client = nil
end
end
#
# Performs cleanup as necessary, disconnecting the HTTP client if it's
# still established.
#
def cleanup
super
disconnect
end
2005-12-25 22:47:38 +00:00
#
# Connects to the server, creates a request, sends the request, reads the response
#
2006-02-06 20:22:47 +00:00
def request ( opts = { } , timeout = - 1 )
c = connect ( opts )
2006-01-27 22:02:35 +00:00
if ( datastore [ 'HTTP::junk_pipeline' ] . to_i > 0 )
c . junk_pipeline = datastore [ 'HTTP::junk_pipeline' ] . to_i
end
request = c . request ( opts )
2006-02-06 22:48:10 +00:00
if ( datastore [ 'HTTP::uri_encode' ] && datastore [ 'HTTP::uri_encode' ] != 'none' )
request . uri_encode_mode = datastore [ 'HTTP::uri_encode' ]
end
2006-01-27 22:02:35 +00:00
if ( datastore [ 'HTTP::chunked' ] == true )
request . auto_cl = false
request . transfer_chunked = true
end
if ( datastore [ 'HTTP::header_folding' ] == true )
request . headers . fold = 1
end
if ( datastore [ 'HTTP::junk_headers' ] == true )
request . headers . junk_headers = 1
end
if ( datastore [ 'HTTP::junk_directories' ] == true )
request . junk_directories = 1
end
if ( datastore [ 'HTTP::junk_slashes' ] == true )
request . junk_slashes = 1
end
if ( datastore [ 'HTTP::junk_params' ] == true )
request . junk_params = 1
end
2006-02-27 19:59:41 +00:00
if ( datastore [ 'HTTP::fake_uri_end' ] == true )
request . junk_end_of_uri = 1
end
if ( datastore [ 'HTTP::fake_param_start' ] == true )
request . junk_param_start = 1
end
2006-01-27 22:02:35 +00:00
if ( datastore [ 'HTTP::junk_self_referring_directories' ] == true )
request . junk_self_referring_directories = 1
end
2006-02-06 20:22:47 +00:00
c . send_request ( request , timeout )
2005-12-25 22:47:38 +00:00
end
2005-09-22 03:24:32 +00:00
##
#
# Wrappers for getters
#
##
#
# Returns the target host
#
def rhost
datastore [ 'RHOST' ]
end
#
# Returns the remote port
#
def rport
datastore [ 'RPORT' ]
end
#
# Returns the VHOST of the HTTP server.
#
def vhost
datastore [ 'VHOST' ]
end
protected
attr_accessor :client
end
###
#
# This module provides methods for exploiting an HTTP client by acting
# as an HTTP server.
#
###
module Exploit::Remote::HttpServer
include Msf :: Exploit :: Remote :: TcpServer
protected
2006-01-02 07:49:52 +00:00
def initialize ( info = { } )
super
register_options (
[
OptString . new ( 'URIPATH' , [ false , " The URI to use for this exploit (default is random) " ] ) ,
2006-01-05 22:20:28 +00:00
] , Exploit :: Remote :: HttpServer
2006-01-27 22:02:35 +00:00
)
2006-01-05 22:20:28 +00:00
2006-01-27 22:02:35 +00:00
register_evasion_options (
[
OptBool . new ( 'HTTP::chunked' , [ false , 'Enable chunking of HTTP responses via "Transfer-Encoding: chunked"' , 'false' ] ) ,
OptBool . new ( 'HTTP::header_folding' , [ false , 'Enable folding of HTTP headers' , 'false' ] ) ,
OptBool . new ( 'HTTP::junk_headers' , [ false , 'Enable insertion of random junk HTTP headers' , 'false' ] ) ,
OptEnum . new ( 'HTTP::compression' , [ false , 'Enable compression of HTTP responses via content encoding' , 'none' , [ 'none' , 'gzip' , 'deflate' ] ] ) ,
] , Exploit :: Remote :: HttpServer
)
2006-01-05 22:20:28 +00:00
2006-01-02 07:49:52 +00:00
end
2006-01-03 04:24:03 +00:00
#
# Ensures that gzip can be used. If not, an exception is generated. The
# exception is only raised if the DisableGzip advanced option has not been
# set.
#
2006-01-27 22:02:35 +00:00
def use_zlib
if ( ! Rex :: Text . zlib_present? and datastore [ 'HTTP::compression' ] == true )
raise RuntimeError , " zlib support was not detected, yet the HTTP::compression option was set. Don't do that! "
2006-01-03 04:24:03 +00:00
end
end
#
# This method gives a derived class the opportunity to ensure that all
# dependencies are present before initializing the service.
#
def check_dependencies
end
2006-01-02 07:49:52 +00:00
2005-09-22 03:24:32 +00:00
#
# This mixin starts the HTTP server listener. This routine takes a few
# different hash parameters:
#
# ServerHost => Override the server host to listen on (default to SRVHOST).
# ServerPort => Override the server port to listen on (default to SRVPORT).
# Uri => The URI to handle and the associated procedure to call.
#
def start_service ( opts = { } )
2006-01-03 04:24:03 +00:00
check_dependencies
2005-09-22 03:24:32 +00:00
# Default the server host and port to what is required by the mixin.
opts = {
'ServerHost' = > datastore [ 'SRVHOST' ] ,
'ServerPort' = > datastore [ 'SRVPORT' ] ,
} . update ( opts )
# Start the HTTP server service.
2006-01-17 04:35:44 +00:00
self . service = Rex :: ServiceManager . start (
Rex :: Proto :: Http :: Server ,
opts [ 'ServerPort' ] . to_i ,
opts [ 'ServerHost' ] ,
{
'Msf' = > framework ,
'MsfExploit' = > self ,
}
)
2005-09-22 03:24:32 +00:00
2006-01-27 05:33:08 +00:00
self . service . server_name = 'Apache'
2006-01-05 22:20:28 +00:00
2005-09-22 03:24:32 +00:00
# Default the procedure of the URI to on_request_uri if one isn't
# provided.
uopts = {
'Proc' = > Proc . new { | cli , req |
2006-02-03 19:56:50 +00:00
evasions ( cli )
2005-09-23 05:51:09 +00:00
on_request_uri ( cli , req )
2005-09-22 03:24:32 +00:00
} ,
2006-01-02 07:49:52 +00:00
'Path' = > resource_uri
2005-09-22 03:24:32 +00:00
} . update ( opts [ 'Uri' ] || { } )
2005-09-23 05:51:09 +00:00
print_status ( " Using URL: http:// #{ opts [ 'ServerHost' ] } : #{ opts [ 'ServerPort' ] } #{ uopts [ 'Path' ] } " )
2005-09-22 03:24:32 +00:00
add_resource ( uopts )
end
#
# Adds a URI resource using the supplied hash parameters.
#
# Path => The path to associate the procedure with.
# Proc => The procedure to call when the URI is requested.
# LongCall => Indicates that the request is a long call.
#
def add_resource ( opts )
2006-01-02 07:49:52 +00:00
@service_path = opts [ 'Path' ]
2005-09-22 03:24:32 +00:00
service . add_resource ( opts [ 'Path' ] , opts )
end
2006-01-02 07:49:52 +00:00
#
# Returns the last-used resource path
#
def get_resource
@service_path
end
2005-09-22 03:24:32 +00:00
#
# Removes a URI resource.
#
def remove_resource ( name )
service . remove_resource ( name )
end
#
# Closes a client connection.
#
def close_client ( cli )
service . close_client ( cli )
end
#
# Creates an HTTP response packet.
#
def create_response ( code = 200 , message = " OK " ,
proto = Rex :: Proto :: Http :: DefaultProtocol )
return Rex :: Proto :: Http :: Response . new ( code , message , proto ) ;
end
2006-01-03 04:24:03 +00:00
2006-01-27 22:02:35 +00:00
#
# Transmits a response to the supplied client, default content-type is text/html
#
# Payload evasions are implemented here!
#
def send_response ( cli , body , headers = { } )
response = create_response
2006-01-02 07:49:52 +00:00
response [ 'Content-Type' ] = 'text/html'
2006-01-27 22:02:35 +00:00
response . body = body
if ( datastore [ 'HTTP::compression' ] )
self . use_zlib # make sure...
response . compress = datastore [ 'HTTP::compression' ]
end
if ( datastore [ 'HTTP::chunked' ] == true )
response . auto_cl = false
response . transfer_chunked = true
end
if ( datastore [ 'HTTP::header_folding' ] == true )
response . headers . fold = 1
end
if ( datastore [ 'HTTP::junk_headers' ] == true )
response . headers . junk_headers = 1
end
2006-01-05 22:20:28 +00:00
2006-01-02 07:49:52 +00:00
headers . each_pair { | k , v | response [ k ] = v }
cli . send_response ( response )
2006-01-27 22:02:35 +00:00
end
2006-01-02 07:49:52 +00:00
#
# Sends a 302 redirect to the client
#
def send_redirect ( cli , location = '/' , body = '' )
response = create_response ( 302 , 'Moved' )
response [ 'Content-Type' ] = 'text/html'
response [ 'Location' ] = location
response . body = body
cli . send_response ( response )
end
#
# Sends a 302 redirect relative to our base path
#
def send_local_redirect ( cli , location )
send_redirect ( cli , get_resource + location )
end
#
# Returns the configured (or random, if not configured) URI path
#
def resource_uri
path = datastore [ 'URIPATH' ] || random_uri
path = '/' + path if path !~ / ^ \/ /
return path
end
2005-09-25 19:47:48 +00:00
2005-09-22 03:24:32 +00:00
#
# Generates a random URI for use with making finger printing more
# challenging.
#
def random_uri
2006-01-02 07:49:52 +00:00
" / " + Rex :: Text . rand_text_alphanumeric ( rand ( 64 ) + 10 )
2005-09-22 03:24:32 +00:00
end
2005-09-24 19:47:56 +00:00
#
# Re-generates the payload, substituting the current RHOST and RPORT with
# the supplied client host and port.
#
2005-09-24 19:37:27 +00:00
def regenerate_payload ( cli )
# Update the datastore with the supplied client peerhost/peerport
datastore [ 'RHOST' ] = cli . peerhost
datastore [ 'RPORT' ] = cli . peerport
# If the payload fails to generate for some reason, send a 403.
if ( ( p = super ( ) ) == nil )
print_error ( " Failed to generate payload, sending 403. " )
cli . send_response (
create_response ( 403 , 'Forbidden' ) )
return nil
end
p
end
2005-09-22 03:24:32 +00:00
##
#
# Override methods
#
##
#
# Called when a request is made to a single URI registered during the
# start_service. Subsequent registrations will not result in a call to
# on_request_uri.
#
def on_request_uri ( cli , request )
end
end
2006-01-27 22:02:35 +00:00
###
#
# This module provides methods for exploiting an HTTP client by acting
# as an HTTP server.
#
###
module Exploit::Remote::HttpServer::Html
include Msf :: Exploit :: Remote :: HttpServer
protected
def initialize ( info = { } )
super
register_evasion_options (
[
2006-02-19 03:58:18 +00:00
# utf-8, utf-7 and utf-7-all are currently not supported by
# most browsers. as such, they are not added by default. The
# mixin supports encoding using them, however they are not
# listed in the Option.
2006-02-10 17:29:37 +00:00
OptEnum . new ( 'HTML::unicode' , [ false , 'Enable HTTP obfuscation via unicode' , 'none' , [ 'none' , 'utf-16le' , 'utf-16be' , 'utf-16be-marker' , 'utf-32le' , 'utf-32be' ] ] ) ,
2006-02-10 15:45:37 +00:00
OptEnum . new ( 'HTML::base64' , [ false , 'Enable HTML obfuscation via an embeded base64 html object' , 'none' , [ 'none' , 'plain' , 'single_pad' , 'double_pad' , 'random_space_injection' ] ] ) ,
2006-01-27 22:02:35 +00:00
OptInt . new ( 'HTML::javascript::escape' , [ false , 'Enable HTML obfuscation via HTML escaping (number of iterations)' , 0 ] ) ,
] , Exploit :: Remote :: HttpServer :: Html
)
end
# Transmits a html response to the supplied client
#
# HTML evasions are implemented here.
def send_response ( cli , body , headers = { } )
2006-02-10 15:45:37 +00:00
if datastore [ 'HTML::base64' ] != 'none'
case datastore [ 'HTML::base64' ]
when 'plain'
body = Rex :: Text . encode_base64 ( body )
when 'single_pad'
body = Rex :: Text . encode_base64 ( ' ' + body )
when 'double_pad'
body = Rex :: Text . encode_base64 ( ' ' + body )
when 'random_space_injection'
body = Rex :: Text . encode_base64 ( body )
new = ''
while ( body . size > 0 )
new += body . slice! ( 0 , rand ( 3 ) + 1 ) + Rex :: Text . rand_text ( rand ( 5 ) + 1 , '' , " \n " )
end
body = new
end
2006-01-27 22:02:35 +00:00
2006-02-10 15:45:37 +00:00
body = '<HTML><BODY><OBJECT ID="' + Rex :: Text . rand_text_alpha ( rand ( 10 ) + 5 ) + '" ' +
'HEIGHT="100%" WIDTH="100%" TYPE="text/html" DATA="data:text/html;base64,' +
body + '">Could not render object</OBJECT></BODY></HTML>'
2006-01-27 22:02:35 +00:00
end
if datastore [ 'HTML::javascript::escape' ] > 0
datastore [ 'HTML::javascript::escape' ] . times {
body = '<script>document.write(unescape("' + Rex :: Text . to_hex ( body , '%' ) + '"))</script>'
}
end
2006-02-19 03:58:18 +00:00
if [ 'utf-16le' , 'utf-16be' , 'utf32-le' , 'utf32-be' , 'utf-7' , 'utf-8' ] . include? ( datastore [ 'HTML::unicode' ] )
headers [ 'Content-Type' ] = 'text/html; charset: ' + datastore [ 'HTML::unicode' ]
body = Rex :: Text . to_unicode ( body , datastore [ 'HTML::unicode' ] )
else
# special cases
case datastore [ 'HTML::unicode' ]
when 'utf-16be-marker'
headers [ 'Content-Type' ] = 'text/html'
body = " \xFE \xFF " + Rex :: Text . to_unicode ( body , 'utf-16be' )
when 'utf-7-all'
headers [ 'Content-Type' ] = 'text/html; charset: utf-7'
body = Rex :: Text . to_unicode ( body , 'utf-7' , 'all' )
when 'none'
# do nothing
else
raise RuntimeError , 'Invalid unicode. how did you get here?'
end
2006-01-27 22:02:35 +00:00
end
super ( cli , body , headers )
end
end
2005-09-22 03:24:32 +00:00
end