# DF-3323 — shared-key auth: cleartext challenge response accepted (auth bypass)

**File:** `sys/netproto/802_11/wlan/ieee80211_hostap.c`
**Severity:** Medium · CWE-287 · confidence: certain · **REPRODUCED on the stock guest kernel**

## What

`hostap_input()` only decrypts an AUTH frame when the Protected bit is set,
and clears the bit before dispatch (`:851-880`). `hostap_auth_shared()`
(`:1122-1150`) processes a seq-3 shared-key response with no knowledge of
whether the frame arrived encrypted, and simply `memcmp()`s the challenge IE
against `ni->ni_challenge` — the exact challenge the AP itself transmitted
**in cleartext** in its seq-2 response (`ieee80211_output.c: add_auth()`
marks frames for encryption only when `arg == IEEE80211_AUTH_SHARED_RESPONSE`,
a seq the AP never sends; frame 2 is legitimately cleartext per
802.11-2016 12.3.3). Frame 3 *must* be protected. The kernel accepts it
unprotected → `ieee80211_node_authorize()` (`:1149`).

An RF-adjacent attacker completes shared-key authentication with **zero
knowledge of the WEP key**: send seq1, hear seq2, echo it back in a
cleartext seq3. Then associate normally (`attack=4`).

## How to reproduce

```
sh findings/poc/DF-3323/build.sh    # compiles the fw4 fake-radio KLD in the guest
sh findings/poc/DF-3323/run.sh      # baseline on stock kernel
```

Success criterion (see `run.log` / `run.2.log`):
```
fw4.attack=2: ... AUTHORIZED=1 ... <<<< BYPASSED
fw4.state[after-assoc]: ... AUTHORIZED=1 associd 0xc001
```
Control (`fw4.attack=3`, corrupted challenge): AP answers seq4 status=15
(STATUS_CHALLENGE) and the node stays AUTHORIZED=0 — the memcmp discipline
works; only the encryption gate is missing.

Impact amplifier (`wepmode mixed`, i.e. DROPUNENC off — also the state of
the natural `authmode shared wepkey …` config without an explicit
`wepmode on`): ONE injected cleartext TODS data frame is delivered to the
AP stack — `netstat -I wlan0` Ipkts 1→2 (`run.2.log`).

## Fix validation

`fix.diff` adds an `else if` in `hostap_input()`'s MGT path: an AUTH frame
that arrives **without** the Protected bit and parses as
algo=SHARED/seq=RESPONSE is discarded (`is_rx_bad_auth++`). Validated on a
rebuilt kernel (`make -j6 nativekernel`, kernel #1 21:14 / re-checked on
the doubly-patched #1 21:29): `fw4.attack=2` no longer authorizes
(AUTHORIZED=0, no seq-4 TX), while `fw4.attack=1` still elicits the seq-2
challenge (legit cleartext frame 1/2 path unbroken). See `fix.log`.
