# DF-0285 — PoC evidence pack

**Finding:** `ieee80211_parse_meshid` heap OOB write via unchecked MESHID IE length.
**File:** `sys/netproto/802_11/wlan/ieee80211_mesh.c:3456-3461`
**Class:** CWE-787 heap overflow (802.11s mesh receive path).
**Severity:** High (remote, wifi-adjacency; function-pointer-control surface).

## TL;DR verdict

**INCONCLUSIVE — real bug, code-level-confirmed; NOT runtime-triggerable on
this guest (no wifi hardware).** The vulnerable function is statically linked
into the live kernel and `IEEE80211_SUPPORT_MESH` is enabled in the GENERIC
config, but `ifconfig -l` shows only `vtnet0 lo0` and there is no
virtual/software frame-injection path into `ieee80211_parse_beacon`. See
`VERDICT.md` for the full trace and the precise function-pointer correction.

## What this pack proves

1. **The overflow is real** — `memcpy(ni->ni_meshid, ie+2, ie[1])` at
   `ieee80211_mesh.c:3460` with `ie[1]` attacker-controlled and unchecked, into
   a fixed 32-byte array. (Source trace in `VERDICT.md`.)
2. **The overflow reaches the `ni_mltimer.toc` pointer** (and a second one,
   `ni_mlhtimer.toc`) — proven by `offsetof()` arithmetic in `layout_proof`
   (numeric, ABI-checked, deterministic). `ni_mltimer.toc` is clobbered for any
   `ie[1] >= 48`; worst case `ie[1]=255` gives a 223-byte OOB write.
3. **The function-pointer claim is corrected**: `struct callout` does **not**
   hold a function pointer directly; it holds `toc`, a pointer to a separately
   allocated `struct _callout` that holds the function pointers. So the
   primitive is a controlled-pointer-deref / type confusion (forge `toc` →
   forged `_callout.qfunc`), not a direct function-pointer field overwrite.
   Real RCE surface, but the finding's one-line wording is slightly imprecise.
4. **The vulnerable code ships in the production kernel** (`nm` on
   `/boot/kernel/kernel` shows `ieee80211_parse_meshid` live); it is merely
   unreachable without a wifi driver+radio.

## Build

```
./build.sh
```
Builds two pure-userspace programs (`layout_proof`, `frame_craft`) with the
guest's base `cc`. No kernel contact.

## Run

```
./run.sh
```
Runs the three-part code-level proof:
1. `layout_proof` — overflow-reach table.
2. `frame_craft` — builds the attacker beacon pcap (`df0285_beacon.pcap`).
3. live-kernel symbol + config + ifconfig check.

## Expected output

* `layout_proof` prints the ABI sanity checks (all `OK`), the field-offset
  table, and the reach matrix showing `ni_mltimer.toc` clobbered for `ie[1]>=48`
  and both callout pointers for `ie[1]>=80`.
* `frame_craft` writes a 341-byte pcap whose MESHID IE is `72 ff 44 46 32 38 35
  41 41 …` (`id=0x72, len=0xFF, "DF285", 'A'*250`).
* the symbol check prints `T ieee80211_parse_meshid`,
  `T ieee80211_mesh_init_neighbor`, `t mesh_peer_timeout_cb`, and
  `ifconfig -l` ⇒ `vtnet0 lo0`.

Because there is no wifi HW, **no kernel panic or memory corruption is
observable at runtime on this guest.** That is the expected, honest result for
a wifi-receive-path bug; the source/layout proof is the deliverable.

## To actually fire it (requires real wifi HW — NOT in this audit)

On a host with a monitor+inject-capable 802.11 radio (e.g. ath9k) and a
DragonFly vap created in MBSS mesh mode:

```sh
# victim: create a mesh vap on a real radio
ifconfig wlan0 create wlandev ath0 wlanmode mesh
ifconfig wlan0 meshid mymesh up

# attacker: replay the crafted beacon at the victim
tcpreplay -i wlan0mon df0285_beacon.pcap    # Linux inject adapter
# or feed df0285_beacon.pcap to the injecting driver directly
```

On receipt, `ieee80211_parse_beacon` stores the IE (`ieee80211_input.c:622`),
`ieee80211_mesh_init_neighbor` (`ieee80211_mesh.c:3471`) calls
`ieee80211_parse_meshid`, and the 223-byte OOB write corrupts `ni_mltimer.toc`
et al.

## Files

| file | purpose |
|---|---|
| `layout_proof.c` | userspace `offsetof()` overflow-reach proof (decisive evidence) |
| `frame_craft.c` | deterministic attacker-beacon pcap builder |
| `df0285_beacon.pcap` | the generated 341-byte payload |
| `build.sh` / `run.sh` | exact build/run |
| `build.log` / `run.log` | full untrimmed outputs |
| `env.txt` | guest environment (uname, cc, kldstat, nm, ifconfig) |
| `VERDICT.md` | full narrative trace + reachability + function-pointer correction |
| `fix.diff` | standalone `git apply`-able clamp fix (validated) |
| `manifest.json` | machine-readable catalog |
