# DF-5845 VERDICT — uid=0 REPRODUCED (weaponized chain, fix validated)

## Bottom line

On the DF-2293 verbatim-interposition harness kernel (stock `fwdev.o`,
`firewire.o`, `fwdma.o` linked verbatim; only the missing OHCI
controller/peer externalities are fabricated), the DF-5845 bug at
`sys/bus/firewire/fwdev.c:561` was converted by an **unprivileged
operator-group user (uid 1002 opuser)** into **uid=0 (root)**:

```
DF5845-WEAPON: uid=1002 gid=1002 S=0xfffff8008bb44b40 ...
DF5845-WEAPON: ioctls done s_rc=-1(22) v_rc=-1(35) uid=0
DF5845-CHAIN-WON: uid=0 euid=0
uid=0(root) gid=1002(opuser) egid=0(wheel) groups=0(wheel)     <- /bin/sh exec
DF5845-CHAIN-WON
exec-proof:
uid=0(root) gid=1002(opuser) egid=0(wheel) groups=0(wheel)
```

`/root/df5845.pwned` (inside the root-only directory) was written by the
post-takeover shell.  The guest survived the winning chain (no panic; the
POST-RCV trace shows both ioctls draining cleanly through stock code).

## The mechanism, line-accurate

1. `fwdev.c:530` `pay_len = MAX(0, asyreq->req.len - tinfo->hdr_len)` —
   attacker-chosen (44 in the weapon; u16-bounded per DF-2296).
2. `fwdev.c:560-561` `bcopy((char *)fp + tinfo->hdr_len,
   (void *)&xfer->send.payload, pay_len)` — writes to the ADDRESS OF the
   pointer field: the weapon's 44 bytes land at S+80..S+123 and plant
   `send.payload := NULL`, `send.pay_len := 0`,
   `recv.payload := S_ADDR-160`, `recv.pay_len := 1024`.
3. `firewire.c:210/220` MAXREC/len gates pass (`send.pay_len` itself is
   smashed to a legal 0); `fw_get_tlabel` assigns a label normally.
4. The peer delivers an RRESB response through the STOCK `fw_rcv`
   (`firewire.c:1731`): `fw_tl2xfer` matches S; `fw_rcv_copy`
   (`firewire.c:1247-`) writes the 640-byte response payload THROUGH the
   smashed `recv.payload` — the arbitrary kernel heap write — covering
   the canary below S, S itself (self-heal: `recv.payload := K`,
   `malloc := M_FWXFER`, `state := FWXF_SENT`,
   `act.hand := fw_asy_callback`), the parked victim V = S+160
   (`state := FWXF_SENT`, `act.hand := ring0 stub`, `send.hdr.dst := 0x22`,
   NULL payloads), and the stub chunk at S+320.
5. `firewire.c:1800-1810` (fw_rcv response branch): rtcode check reads the
   planted header, `oldstate` (planted FWXF_SENT) routes to
   `fw_xfer_done` → planted `fw_asy_callback` wakes S's ioctl; S walks
   `fwdev.c:570-583` (the response-copy branch runs even in the EINVAL
   case — 44 bytes copied from K, exactly as traced) and drains via
   `fw_xfer_free_buf` with all-freed-or-NULL payload pointers.  Clean.
6. The peer delivers V's (len=0) response: `fw_tl2xfer` matches V by the
   PLANTED `send.hdr.dst`; planted `FWXF_SENT` → `fw_xfer_done(V)` →
   **`V->act.hand(V)` = the 88-byte stub in kernel heap**:
   `pfindn(weapon_pid)` → `p->p_ucred` → zero `cr_uid/cr_ruid/cr_svuid`,
   `cr_ngroups := 1`, `cr_groups[0] := 0`; returns cleanly.
7. The still-unprivileged weapon process sees `getuid() == 0` and
   `exec`s `/bin/sh` — full credential takeover.

Kernel forensics captured in `run.dmesg.full.txt`: the POST-RCV dump of
the winning attempt shows V `state=3 act.hand=0xfffff8008bb44c90` (the
stub, planted by the write) before the V-response and
`state=10 resp=90` after — 90 = 0x5A is the stub's entry marker written
into `V->resp` at dispatch time.  Proof the forged `act.hand` was called
from stock `fw_xfer_done`.

## Threat model / legitimacy framing (honest)

- The corruption, the arbitrary write, the dispatch and the credential
  overwrite all execute in STOCK kernel binaries (`fwdev.o`,
  `firewire.o`) reached by a single operator-group ioctl — the exact
  attack surface of a FireWire-equipped host.
- The harness supplies only what real hardware would: AT TX-completion
  semantics (fwohci.c:1110) and AR packet delivery into stock `fw_rcv`
  (as `fwohci_arcv` does).  The root-only rig plays the malicious
  FireWire peer — attacker-controlled hardware that crafts response
  packets.  On real hardware the identical packets arrive over the bus.
- Rig conveniences beyond the strict peer model: heap-shaping canaries
  (any local user can spray the same zone via ordinary syscalls), the
  boot-deterministic symbol addresses (no KASLR; same information is in
  the world-readable kernel binary), and CPU pinning for determinism
  (the launcher pins; the weapon binary itself uses no privilege).
- The `pfindn`-based stub means the chain does not even need SMEP/SMAP
  to be disabled: the stub lives in kernel heap.  The guest is
  permissive, but nothing in the chain depends on user pages being
  executable.

## Engineering history (what it took — full logs in git-ignored attempt dirs)

Five kernel-side/userland iteration rounds, each failure captured:
1. First injection: response write worked, but `fwdev.c:583` copied from
   a NULL `recv.payload` (rig planted K=0 because spray idx 0 was
   popped) → `Fatal trap 12 in memmove <- fw_ioctl` — proved the write
   path end-to-end.
2. Timeout path double-free of a LIVE V (rig failed to inject on the
   first watermark bug) → `memory chunk ... already free` — led to the
   S-160 write-base geometry.
3. `S-48` interior-pointer early-free poisoned the freelist with a
   misaligned chunk → delayed `chunk_mark_free` assertion panic.
   Replaced by the chunk-aligned S-160 canary below S.
4. PEND watermark semantics (`seq <= seq_after` cannot express
   report-all; warm post + baseline-before-ready-wait + `<` filter).
5. Stub v1 encoded disp8 offsets 160/164 as SIGN-EXTENDED (-96/-92) —
   the cred walk corrupted ucred-96 of adjacent objects (a later `sh`
   panic) while uid stayed 1002.  v2 uses disp32 everywhere plus
   entry/exit markers; first v2 run converted.

Also on the path: canary-spray cap exhaustion (512) handled by per-attempt
unspray; shape gate (allocate-back-verify) to make the S/V pair
deterministic; always-respond fix-up so aborted attempts drain cleanly
instead of timing out.

## Fix validation (A/B on the same rig)

`fix.diff` = drop the `&` at fwdev.c:561 **and** the DF-2296 pay_len bound
(EINVAL before `fw_xfer_alloc_buf`).  Applied in the guest `/usr/src`,
kernel rebuilt (kernel #5), rebooted, and the IDENTICAL weapon + rig +
packets re-run (3 attempts, two full injections delivered):

```
DF5845-WEAPON: uid=1002 ... uid=1002      (no takeover)
RIG: wire: S=... V=... ; S-response delivered; V-response delivered
dmesg panic/Fatal count: 0 ; guest uptime continues
```

With the fix the 44 bytes land in the REAL send.payload buffer,
`recv.payload` is never smashed, `fw_rcv_copy` copies the response into
the real 4096-byte recv buffer (in-bounds), V keeps its real
`fw_asy_callback`, both ioctls complete through stock code and nothing
escalates.  Baseline kernel #4 + same inputs → uid=0.  The fix kills the
chain and leaves the legitimate path intact.

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
