Sploitus

Exploit for clipwire

githubexploit Β· 2026-08-13

Exploit Code

README104 lines
## https://sploitus.com/exploit?id=92CF8784-F2D4-5E44-8D6D-5D73C9E70D31
# clipwire

Kernel exploit development harness for the **iPhone 11 (A13, `iPhone12,1`)** on
**iOS 26.1 (23B85)**.

> **Disclosure status.** Both bugs were found by patch-diffing xnu 26.1 (42.6)
> against 26.6 (162.13) and are **patched in iOS 26.6** β€” Apple's fix was already
> shipping when this harness was written. They are disclosed here after the
> patch, in the spirit of public exploit research. No CVE has been assigned to
> either bug. This code is a development harness, not a validated end-to-end
> jailbreak: see [Status / caveats](#status--caveats).

Targets two bugs found by patch-diffing xnu 26.1 (42.6) against 26.6 (162.13):

| Stage | Bug | 26.1 symbol (unslid) | 26.6 fix |
|-------|-----|----------------------|----------|
| Bug 1 | Unlocked / stale-entry `vm_map_clip` in the CoW submap fault path | `0xfffffff0080b8820` (`KSYM_COW_FAULT_FUNC`) | clip re-validates entry bounds after new-entry allocation and panics |
| Bug 2 | `vm_fault_wire_object_pages` has no `offset + wire_size size` check | `0xfffffff0080abae0` (`KSYM_WIRE_OBJECT_PAGES`) | 26.6 added the bounds check + panic |

The clip path locks the copy-map, **releases it**, then clips an entry taken from
the saved slot with the submap's own lock **not held** and no bounds re-check.
The wire loop walks `vme_start..vme_end` and decodes the object from the entry's
`+0x38` upper-32 phys index into the `0xffffffdc00000000` window, storing
`vm_page` pointers into the object's page array with no object-size bounds check.

## Layout

```
clipwire/
  src/clipwire.h      constants, entry layout, tunables, API
  src/util.c          init, device/version check, helpers
  src/slide.c         kernel slide (env/sysctl fallbacks; prim scan in krw.c)
  src/trigger.c       Bug 1: COW submap + two-thread clip race
  src/wire.c          Bug 2: wire-OOB driver + IOSurface grooming + victim detection
  src/krw.c           phys r/w via corrupted page array, kread/kwrite,
                      phys-scan slide leak, proc discovery, cred escalation
  src/exploit.c       orchestration
  src/main.m          entry point
  Makefile            clang (SDK) + ldid build
  entitlements.plist  sandbox/IOSurface/IOKit exceptions
```

## Chain

1. **Bug 1** `vm_remap(copy=TRUE)` builds a COW submap; two threads race the
   unlocked clip in `0xfffffff0080b8820` (copy-map unlocked at `0x8c2c`, then
   clip on the saved entry with no submap lock). Detected via
   `mach_vm_region` (entry grew / region start moved).
2. **Bug 2** wiring the corrupted range runs `0xfffffff0080abae0`, which walks
   `vme_start..vme_end` with no object-size check, writing `vm_page*` past the
   victim vm_object's page array into the **kernel heap**. Grooming targets
   **heap-adjacent vm_object page arrays** (COW copies of the same page count),
   plus an IOSurface phys-contiguous pool for the phys-window case.
   `verify_wire_landed` uses a write-through test: after the OOB, a groom whose
   array was overwritten maps the victim's pages, so a stamp written via the
   groom's user VA shows up in the victim's wired pages - that identifies the
   victim with no kernel read.
3. **KRW** `krw.c` resolves every owned page (victim range + COW grooms +
   IOSurface pool) to its phys via `mach_vm_page_info` and builds a `(va,phys)`
   alias cache (`prim_establish`). The write-through alias means the *same*
   phys is reachable through two user VAs, which `escalate()` proves before
   anything else. `prim_read64`/`prim_write64` serve the achievable alias
   space; a request for kernel phys (outside our pages) is reported, not
   guessed. If a phys-window object is ever confirmed in the OOB path, the
   `prim_find_kernel_in_phys` scan turns that into slide + kread/kwrite through
   the kernel linear map. `find_proc_by_comm` self-verifies `struct proc`
   (p_list kernel ptrs + p_pid == getpid()), and `patch_creds` self-verifies
   the ucred (cr_ref small, cr_uid == getuid()) before zeroing uid/gid.

## Build / run (macOS + jailbroken device over SSH)
```sh
make            # build arm64e clipwire binary
make sign       # ldid -S entitlements.plist
make deploy DEVICE_IP=1.2.3.4   # scp + run over ssh
CLIPWIRE_SLIDE=0x1234000 /var/tmp/clipwire   # inject slide for pre-primitive tests
```

Tunables (`COW_SRC_PAGES`, `RACE_ITERATIONS`, `GROOM_OBJECTS`, ...) live in
`src/clipwire.h`. Log output goes to stdout.

## Status / caveats

- All C sources pass a pycparser syntactic parse (no compiler on this host);
  build on macOS with `make sign`.
- On-device work left: confirm the phys alias resolution reports
  (the `physical_page` field of `mach_vm_page_info`), and, if kernel phys is
  reachable, a phys-window object in the OOB path. The `struct proc`/`ucred`
  candidate offsets in `krw.c` are self-verified at runtime, so the exact
  values are tolerant.
- `DEVICE_PHYS_BASE` (A13) needs one on-device confirmation; the phys scan
  itself is base-independent.
- The exact race interleaving that lands the stale clip is unverified at
  runtime; `RACE_ATTEMPTS` / `RACE_ITERATIONS` tuning is expected on-device.
- Sandboxed reachability of the wire path is unconfirmed; `mlock` first,
  `vm_wire` second.
- This project is for authorized research on devices you own. It will panic a
  26.1 device when the OOB lands (that is the point).

## License

Restricted research license β€” see [LICENSE](LICENSE). Free for security
research and analysis; deployment against unauthorized targets, malware, and
commercial exploitation require separate written authorization.