# DF-0326 — ieee80211_node unbounded SSID heap overflow

**Severity:** High (CWE-787 Out-of-bounds Write)
**Class:** Remote, unauthenticated heap overflow via crafted 802.11 Beacon SSID IE.
**Status:** Real shipped bug in the live GENERIC kernel; unreachable on the KVM
audit guest (no 802.11 radio). See `VERDICT.md` for the full analysis.

## The claim (DB-confirmed)

`ieee80211_sta_join` (`sys/netproto/802_11/wlan/ieee80211_node.c:815-816`) and
`ieee80211_init_neighbor` (`:1521-1522`) copy `sp->ssid[1]` (attacker-controlled
0–255, from a Beacon/Probe-Response SSID IE) into `ni_essid[32]` via `memcpy`
with **no** check that `ssid[1] <= IEEE80211_NWID_LEN (32)`. Rate sets ARE
guarded (`scan_sta.c:285` KASSERT); the SSID is NOT. A single crafted Beacon
with `ssid[1]=192` overflows 160 bytes past `ni_essid` into `ni_rates` and the
`ni_chan` kernel pointer. STA/IBSS/Mesh/TDMA all affected.

## What's in this folder

| file              | purpose                                                                 |
|-------------------|-------------------------------------------------------------------------|
| `VERDICT.md`      | full narrative: line-by-line proof, dead-flag analysis, offset table     |
| `node_overflow.c` | userspace replica of `struct ieee80211_node` tail; runs the exact memcpy and prints which fields (incl. `ni_chan` pointer) get clobbered for ssid_len ∈ {32,48,56,192,255} |
| `beacon_gen.c`    | emits a raw 802.11 Beacon frame with a weaponized SSID IE (default len=192) |
| `beacon.bin`      | the crafted beacon produced by `beacon_gen 192` (239 bytes) — inject on real radio HW |
| `build.sh`        | `cc -O2 -Wall` both programs                                            |
| `run.sh`          | runs the overflow proof, the beacon generator, and the live-kernel symbol/reachability probe |
| `build.log`       | full untrimmed final build output                                       |
| `run.log`         | full untrimmed final run output                                         |
| `env.txt`         | guest uname, cc version, kldstat, ifconfig -l, `nm` symbol checks, and the host-side proof that `BPARSE_SSID_INVALID` is never consumed |
| `fix.diff`        | standalone `git apply`-able diff clamping SSID len at all three sinks   |
| `manifest.json`   | machine-readable catalog                                                |

## Build & run (on the guest, no wifi required for the code-level proof)

```
ssh dfbsd-maxx   # or copy the folder to any DragonFlyBSD host
cd poc/DF-0326
./build.sh && ./run.sh
```

### Expected output (decisive lines)

From `node_overflow`:
```
ni_essid[32]           off=29   size=32   [SINK]
ni_rates               off=61   size=16
ni_chan (POINTER)      off=80   size=8    (kernel POINTER)
ni_essid -> ni_chan distance = 51 bytes
=> an SSID of length >= 59 fully overwrites ni_chan.

=== overflow with ssid_len = 192 (ni_esslen now 192) ===
  ni_essid[32]  ...  [SINK] overflows past end
  ni_rates              ...  CLOBBERED
  ni_chan (POINTER)     ...  CLOBBERED ptr=0xcccccccccccccccc  <-- ATTACKER-CONTROLLED KERNEL POINTER
  ... 160 byte OVERFLOW; 8/8 bytes of the channel pointer are attacker-controlled
```

From `run.sh` reachability probe:
```
ffffffff8078c4c0 T ieee80211_add_scan
ffffffff8077dc50 T ieee80211_init_neighbor
ffffffff8077d1f0 T ieee80211_sta_join
--- 802.11 interfaces present (radio check) ---
vtnet0 lo0
```
i.e. the vulnerable code IS compiled into the live kernel, but there is no wlan
interface to drive it — hence `inconclusive` (real bug, no radio).

## Live reproduction (requires real 802.11 hardware)

```
./beacon_gen 192 > beacon.bin
# inject beacon.bin from a monitor+injection-capable wlan vap (e.g. scapy,
# lorcon, or a custom raw socket) while the victim STA/IBSS/Mesh is scanning.
# expect: kernel panic from corrupted ni_chan deref, or silent heap corruption.
```

## Notes

- The `IEEE80211_VERIFY_ELEMENT(...,SSID_INVALID)` at `ieee80211_input.c:680`
  looks like a guard but is **not**: the `IEEE80211_BPARSE_SSID_INVALID` bit it
  sets is **never read by any consumer** (grep confirms only set + define; see
  `env.txt`). The rate flag and off-channel flag ARE consumed; the SSID flag is
  dead. That is the root-cause asymmetry.
- `node_overflow.c` is line-faithful to `ieee80211_node.h:176-196`; offsets are
  the real LP64 layout (pointer = 8 bytes, `x86_64` confirmed by `uname`).
