DragonFlyBSD Kernel Audit
← triage · dashboard
DF-0275

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0275 · 8 files
FileTypeDescriptionSize
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
README.md readme claim, runtime status, reproduce (needs WiFi HW), fix
↓ download 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.

VERDICT.md verdict source trace: 100-byte slot vs 2+ie[1] up to 1024 write; overflow math table
↓ download raw

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_WPA ioctl, 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_testable

compile 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.