# CVE-2022-41128: Type confusion in Internet Explorer's JScript9 engine

JScript9 JIT type confusion lets a swapped Int32Array/Object write out of bounds, giving relative then arbitrary R/W and code execution via fake vtable.

- Payout: Real-world hack
- Severity: high
- Program: Microsoft Windows
- Credited: Benoît Sevens and Clément Lecigne, Google's Threat Analysis Group (TAG)
- Disclosed: 2022-11-08
- CVE: CVE-2022-41128
- Write-up quality: 9/10
- Original: https://github.com/googleprojectzero/0days-in-the-wild/blob/main/0day-RCAs/2022/CVE-2022-41128.md
- Library page: https://bugbountylibrary.com/r/p0%3ACVE-2022-41128
- Tags: domain:browser impact:rce kind:incident platform:project-zero sev:high tech:windows technique:chained technique:source-review vuln:memory-corruption

**Target:** Internet Explorer JScript9 engine (jscript9.dll) invoked via Office HTML rendering
**Root cause:** GlobOpt::OptArraySrc calls ShouldExpectConventionalArrayIndexValue and wrongly skips code, so the JIT assumes variable q keeps its Int32Array type across the function even after reassignment to an Object, using Int32Array offsets on an Object.
**Steps:**
1. Trigger: `q = d` (Int32Array), then inside nested loop reassign `q = e` (Object) and write `q[-1]=1`, then `q[0]=0x42424242`; JIT still treats q as Int32Array and writes at e.d/e.e offset.
2. Warm up `boom(false)` 100000 times, then call `boom(true)`.
3. Heap groom: allocate `b = new Array(256)` of inline 16-element arrays; set `e.d = b[52]`.
4. Trigger writes `q[8]=0x1fffffff` to overwrite b[52] length; read `q[0]`,`q[1]` to leak b[52] vtable pointer (jscript9.dll base).
5. Set `b[53][0] = dataview` to force buffer realloc; leak b[53] buffer address via OOB read from b[52]; overwrite b[54] buffer pointer to alias b[53] buffer.
6. Use b[54] to redirect dataview buffer to arbitrary address: `read4`/`write4` via `dataview.getUint32/setUint32`.
7. Walk jscript9.dll base, parse import table to kernel32, parse export table for VirtualProtect.
8. Build fake literal string object in shellcode string buffer with fake vtable in b[56] buffer; vtable entry for GetOriginalStringReference = VirtualProtect.
9. Call `b[53][1]['trim']()` to invoke VirtualProtect(shellcode, 2900, 0x40, ...); then swap vtable entry to shellcode address and call again.
**How found:** Not stated for this bug; authors note high similarity to CVE-2021-34480 (Ivan Fratric, found via fuzzing) and suggest variant analysis or fuzzing from that PoC; patch analysis points to flawed check in GlobOpt::OptArraySrc / ShouldExpectConventionalArrayIndexValue.
**Exploitation:** Office document loads remote HTML executed by IE JScript9; type confusion yields relative R/W, leaks vtable for ASLR defeat, corrupts DataView for arbitrary R/W, builds fake literal string with fake vtable calling VirtualProtect to mark shellcode executable (CFG bypass since VirtualProtect is valid target and marks pages valid call targets), then executes shellcode with user privileges (Office unsandboxed by default). Requires user to disable Protected View.
**Escalation:** Relative R/W -> arbitrary R/W via DataView buffer pointer corruption -> fake vtable VirtualProtect call -> shellcode execution; CFG bypassed because VirtualProtect marks pages as valid call targets.
**Insight:** JIT type checks at function entry don't guarantee type stability across loops; look for optimizer paths that skip re-validation when a variable is reassigned inside nested loops, and use array-length corruption plus DataView buffer-pointer aliasing for arbitrary R/W.
