Heap buffer overflow in WPA/RSN IE construction: variable-length IE written into fixed sizeof(ieee80211_ie_wpa)=100 slot
Summary
add_ie(:1976) memcpy(frm,ie,2+ie[1]) trusts ie length unconditionally. Frame allocators reserve fixed sizeof(ieee80211_ie_wpa)=100 bytes. ioctl permits ie[1] up to 1022 (IEEE80211_MAX_APPIE). IE>98 bytes -> heap overflow in mbuf data area. Remote trigger: AP sends probe response for every probe request -> add_rsn/add_wpa overflow. WiFi-management capability to set IE first.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0275 · 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | source trace: 100-byte slot vs 2+ie[1] up to 1024 write; overflow math table | 4.6 KB | ↓ raw |
| README.md | readme | claim, runtime status, reproduce (needs WiFi HW), fix | 1.7 KB | ↓ raw |
| fix.diff | suggested-fix | cap ie[1] in setwparsnie to sizeof(ieee80211_ie_wpa)-2 | 841 B | view raw |
| build.sh | build-script | validates fix.diff applies cleanly | 478 B | view raw |
| run.sh | run-script | N/A on this guest | 343 B | view raw |
| fix_build.log | build-log | nativekernel -Werror compile-validation of ieee80211_ioctl.c | 696 B | view raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | ↓ download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-0275 — Heap buffer overflow in WPA/RSN IE construction
Claim
add_ie() (ieee80211_output.c:1976) does memcpy(frm, ie, 2+ie[1]) trusting
ie[1] unconditionally. Frame allocators reserve a fixed
sizeof(struct ieee80211_ie_wpa)=100-byte slot, but the IEEE80211_APPIE_WPA
ioctl permits ie[1] up to 1022 (IEEE80211_MAX_APPIE=1024). An IE with
ie[1] > 98 overflows the mbuf data area by up to ~924 attacker-controlled bytes.
Runtime status on this guest
NOT runtime-testable — the guest has no 802.11 radio hardware and cannot
create an ieee80211vap, so neither the privileged IE-install ioctl nor the
management-frame TX path can be exercised. The bug is definitively confirmed
by source trace (see VERDICT.md): sizeof(struct ieee80211_ie_wpa)=100,
ie[1] can reach 1022, add_ie writes 2+ie[1] bytes.
Reproduce (requires WiFi hardware / a vap)
On a system with a WiFi interface in hostap/ibss/sta mode (root to set the IE):
1. Create/bring up a vap.
2. ifconfig wlan0 -wpa / set the WPA app-IE via IEEE80211_APPIE_WPA with an
IE whose length byte > 98 (up to 1022).
3. Trigger a management-frame TX that includes the WPA/RSN IE (e.g. a probe
response to an incoming probe request). → heap overflow / panic on GENERIC.
There is no runnable PoC in this folder because the defect cannot be exercised
without WiFi hardware; the authoritative evidence is the source trace in
VERDICT.md and the fix in fix.diff.
Build / Run
N/A on this guest (no WiFi HW). fix.diff applies + compiles cleanly
(nativekernel).
Fix
fix.diff caps ie[1] in setwparsnie() to sizeof(struct ieee80211_ie_wpa)-2
so a stored IE always fits the fixed frame-constructor slots.
DF-0275 — VERDICT
Verdict: NOT RUNTIME-TESTABLE on this guest (no WiFi hardware) — source trace DEFINITIVELY CONFIRMS the bug is real.
The claim
sys/netproto/802_11/wlan/ieee80211_output.c, add_ie() at line 1976:
1976: static __inline uint8_t *
1977: add_ie(uint8_t *frm, const uint8_t *ie)
1978: {
1979: memcpy(frm, ie, 2 + ie[1]); /* trusts ie[1] unconditionally */
1980: return frm + 2 + ie[1];
1981: }
add_ie() is called by ieee80211_add_wpa()/ieee80211_add_rsn() (lines 2165,
2176) to write the vap's WPA/RSN IE (vap->iv_wpa_ie/vap->iv_rsn_ie) into a
management frame. Every frame constructor that includes a WPA/RSN IE reserves a
fixed sizeof(struct ieee80211_ie_wpa) slot, e.g. ieee80211_send_probereq
(line 2244):
2244: m = ieee80211_getmgtframe(&frm,
2245: ic->ic_headroom + sizeof(struct ieee80211_frame),
2246: 2 + IEEE80211_NWID_LEN
2247: + 2 + IEEE80211_RATE_SIZE
2248: + sizeof(struct ieee80211_ie_wpa) /* <-- fixed 100-byte slot (RSN) */
2249: + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
2250: + sizeof(struct ieee80211_ie_wpa) /* <-- fixed 100-byte slot (WPA) */
...);
The overflow math (confirmed from source)
struct ieee80211_ie_wpa (sys/netproto/802_11/ieee80211.h:567) is the
maximally-sized WPA IE, __packed:
| field | bytes |
|---|---|
| wpa_id | 1 |
| wpa_len | 1 |
| wpa_oui[3] | 3 |
| wpa_type | 1 |
| wpa_version (u16) | 2 |
| wpa_mcipher[1] (u32) | 4 |
| wpa_uciphercnt (u16) | 2 |
| wpa_uciphers[8] | 32 |
| wpa_authselcnt (u16) | 2 |
| wpa_authsels[8] | 32 |
| wpa_caps (u16) | 2 |
| wpa_pmkidcnt (u16) | 2 |
| wpa_pmkids[8] (u16) | 16 |
| total | 100 |
So the frame allocators reserve exactly 100 bytes per WPA/RSN IE slot.
The IE pointer comes from setwparsnie() (ieee80211_ioctl.c:2319), invoked by
the IEEE80211_APPIE_WPA ioctl handler (ieee80211_ioctl.c:2384) on data
allocated by setappie() (ieee80211_ioctl.c:2296) with size
sizeof(struct ieee80211_appie) + ireq->i_len, where the ioctl layer enforces
only 2 <= ireq->i_len <= IEEE80211_MAX_APPIE (IEEE80211_MAX_APPIE = 1024,
ieee80211_ioctl.h:500). setwparsnie checks 2+ie[1] > space but does not
cap ie[1] to what the frame buffers can hold, so ie[1] (the WPA length byte)
can be as large as 1022.
add_ie() then does memcpy(frm, ie, 2 + ie[1]) = up to 2 + 1022 = 1024
bytes into a 100-byte slot → up to ~924 bytes of heap OOB write into the
mbuf data area, with fully attacker-controlled content (the IE bytes are
copyin'd from userspace).
Trigger / reachability
- The oversized IE must first be installed via the
SIOCS80211/IEEE80211_APPIE_WPAioctl, which is a privileged (root) wlan-management operation. So the setup is root-gated. - Once installed, the overflow fires on the next TX of any management frame that includes the WPA/RSN IE — beacons, probe responses, (re)assoc responses. For an AP, a remote station's probe request triggers a probe-response TX that overflows. So the amplification is remotely triggerable; the setup is not.
Why not runtime-tested here
This guest has no 802.11 radio hardware and cannot create an ieee80211vap,
so neither the privileged IE-install ioctl nor the management-frame TX path can
be exercised. The overflow is therefore not runtime-testable on this guest,
but the code path and the byte arithmetic are unambiguous and definitively
confirm the defect.
Realistic impact ceiling
Heap OOB write of up to ~924 attacker-controlled bytes into the mbuf data area on management-frame TX, with root-gated setup. On the default GENERIC kernel (INVARIANTS ON) this manifests as a panic when the corrupted mbuf/slab is later validated; on an INVARIANTS-OFF build it is a silent heap corruption primitive. Because the trigger requires root to install the IE, this is a root→kernel hardening/corruption gap, not an unprivileged→root escalation.
Fix
findings/poc/DF-0275/fix.diff adds a length cap in setwparsnie()
(ieee80211_ioctl.c): reject any IE whose ie[1] would exceed
sizeof(struct ieee80211_ie_wpa) - 2 (= 98), so the stored IE always fits the
fixed frame-constructor slots. Verified git apply --check clean and compiles
into the base kernel (nativekernel, together with the DF-0286/0287 mesh fixes).
Because the bug cannot be runtime-exercised here, fix_status: not_testable
(diff applies + compiles; path traced to close the defect).
Fix verification
not_testablecompile validated
module/kernel build rc=0
Confirmed kernel references
—
Detail
Exploit chain
none
Evidence (decisive lines)
—
Verdict
Source-confirmed. WPA/RSN IE add_ie memcpy 2+ie[1] up to 1024 into 100B slot -> ~924B heap OOB. Root-gated ioctl. No WiFi HW.
No comments yet.