|
|
Message-ID: <CAK3hNHa4DECq3yFmJt-OFCuJE_tr7dvz3aXto7msVvjOETXK6A@mail.gmail.com>
Date: Sat, 1 Aug 2026 23:29:40 -0700
From: Abhinav Agarwal <abhinavagarwal1996@...il.com>
To: oss-security@...ts.openwall.com
Subject: [CVE requested] iwd <= 3.12: stack buffer overflow in the 802.11k
beacon report handler, plus three parser/validation bugs (no fix upstream)
An adjacent attacker can overflow iwd's stack with a spoofed 802.11k request
once 17 BSS entries are cached. Hardened builds abort; a representative
unhardened ARM32 layout permits saved-LR control. No network credentials
are required on WPA2 without PMF.
One maintainer reviewed and confirmed the findings and patches, verified
them against the spec, and applied them locally - but they remain unmerged.
Affected: all four coexist in 1.30 through 3.12 (latest release).
They arrived separately, so older versions have a subset.
Repository: https://git.kernel.org/pub/scm/network/wireless/iwd.git
Write-up: https://abhinavagarwal07.github.io/posts/iwd-rrm-stack-overflow/?src=oss
PoC: https://github.com/abhinavagarwal07/iwd-security-poc
1. RRM beacon report stack buffer overflow
CWE-121. CVSS 3.1: 8.8 High
CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
src/rrm.c:334 and :370, rrm_report_beacon_results()
Introduced 1.1, commit 1f01819c701e
rrm_report_beacon_results() builds a Radio Measurement Report action
frame in uint8_t frame[512] on the stack. The loop appends one 31-byte
record per matching BSS - a 5-byte Measurement Report IE header plus a
26-byte packed rrm_beacon_report - and advances the write pointer with
"ptr += report_len + 5" and no bounds check. After the 3-byte action
header, 16 records fit. Record 17 writes frame[499..529]. The BSS list
is the station's own scan cache, so the count is a property of the RF
environment - but an attacker can deterministically reach the threshold
by transmitting additional fake beacons.
rrm_frame_watch_cb() requires STATION_STATE_CONNECTED (rrm.c:727), then
does a bare memcmp of the frame's address_2 against the connected AP's
MAC (rrm.c:733). There is no PMF requirement, so on WPA2 without
802.11w that source address is whatever the attacker puts in it. One
request clears all three per-BSS filters: a wildcard BSSID
(ff:ff:ff:ff:ff:ff) skips the BSSID compare, omitting the SSID
sub-element skips the SSID compare, and mode=TABLE permits channel=0,
which makes bss_in_request_range() return true unconditionally. Every
cached BSS is then reported.
Request fields and attacker-generated beacon metadata make parts of each
overflowing record attacker-controlled.
Outcomes are build-dependent:
* Hardened distro builds abort in __stack_chk_fail.
* In the tested no-canary x86-64 build, the zeroed scan_start_time
field overwrote saved RIP, producing a crash without control.
* In a representative unhardened ARM32 layout, the saved lr is
reachable and holds attacker bytes. I demonstrated pc hijack on
armhf in a harness reproducing that frame layout (exit code 42 from
win()). That shows the primitive reaches the return address on the
ABI; it is not an exploit against a shipped binary, since the record
and field that land on lr depend on the build's register allocation.
FORTIFY_SOURCE does not help. Each individual memcpy is in bounds; the
overflow comes from the loop accumulating them.
Evidence - AddressSanitizer on a real iwd 3.12 daemon, driven over
mac80211_hwsim with 21 cached BSSes and the action frame sent via
NL80211_CMD_FRAME:
==67193==ERROR: AddressSanitizer: stack-buffer-overflow
WRITE of size 26 at 0x7ffe8136a2a0 thread T0
#3 in rrm_report_beacon_results src/rrm.c:367
[384, 896) 'frame' (line 334) <== Memory access at offset 896
overflows this variable
2. HE Capabilities validator reads the wrong byte
CWE-125. CVSS 3.1: 4.3 Medium
CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
src/ie.c:2714 vs src/band.c:631
Introduced 1.30, commit 53988a728533
The Channel Width Set is bits B1-B7 of PHY Capabilities byte 0, element
body offset 6 (802.11ax-2021 s9.4.2.248.3). The validator
ie_validate_he_capabilities() reads offset 7 while
band_estimate_he_rx_rate() and wiphy.c read offset 6 from the same
pointer, so its length guards (len < 26 for the 160 MHz MCS map, len <
30 for 80+80) are keyed on the wrong byte. A
22-byte body with hec[6]=0x1C and hec[7]=0x00 passes validation, then
drives the estimator into both branches, reading hec[23..24] and
hec[27..28]. The local adapter must also advertise the width, since the
two sets are ANDed.
Passive scanning reaches this path before association. In a real beacon
the OOB reads consume attacker-controlled bytes from the following
element, influencing the rate used for BSS ranking. Only the HE
Capabilities element must be malformed.
The fix is one character: (ptr + 7) -> (ptr + 6).
3. mde_equal() compares its first argument to itself
CWE-697. CVSS 3.1: 3.1 Low
CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N
src/ft.c:385
Introduced 1.14, commit 2c0234e1
return memcmp(mde1, mde1, mde1[1] + 2) == 0;
mde2 is never read, so the check always passes. Its only caller is
ft_parse_ies() (ft.c:667), where it should confirm the target AP echoed
the Mobility Domain element the station sent on the 802.11r FT
Authentication Response and FT-over-DS paths. Not an authentication
bypass - __ft_rx_associate() compares correctly at ft.c:496 and the FTE
MIC there is keyed on PMK-R1 - but a response carrying an arbitrary
mobility domain ID and FT capability flags passes the authentication
stage into FTE parsing and key derivation. Finding 4 sits on that path,
one call later.
4. FTE sub-element length underflows a uint8_t
CWE-191 -> CWE-125. CVSS 3.1: 3.1 Low
CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L
src/ie.c:1919, ie_parse_fast_bss_transition()
Introduced 0.1, commit e4c168cc
The sub-element loop never checks the declared length against the bytes
remaining. The case handlers copy on the declared length alone, so a
6-byte R1KH-ID copy runs with zero bytes left, and "len -= subelem_len +
2" on a uint8_t wraps rather than going negative (2 - 8 == 250), so the
loop keeps walking past the element. The declared-length checks bound
every write into the fixed-size ie_ft_info fields, so there is no
out-of-bounds write; the reads are unbounded. The scored impact is the
read running off the end, not disclosure: ft_parse_fte() then requires
the parsed r0khid to equal the station's own (ft.c:361), so corrupted
values are discarded rather than echoed back, and I have no extraction
channel.
ft_parse_fte() parses before it validates (ft.c:353 parse, ft.c:357
checks), on the FT Authentication Response path where the FTE carries no
MIC at all - the code requires mic_element_count == 0. Nothing
cryptographic sits between a rogue AP's response and the parser. The
reassociation path has the same ordering (parse ft.c:513, MIC ft.c:524).
Reaching it needs the station to attempt an FT roam to a BSS the attacker
controls. Finding 3 removes the MDID check on that same path.
Who this affects:
Hardened desktop packages are expected to abort through the stack canary.
Embedded builds without stack protection may expose control-flow
corruption; on Yocto and Buildroot that is a per-image configuration
choice.
SteamOS uses iwd by default - steamos-customizations-git ships
/usr/lib/NetworkManager/conf.d/10-steamos-defaults.conf with
"wifi.backend=iwd" - and currently ships iwd 3.9-1.2, which contains all
four defects. Not tested but appears to be affected. PSIRT was notified.
Mitigation:
The PoC repository contains four independently applicable patches. See
patches/README.md for backport notes.
Coordination timeline:
2026-05-12 Reported to Intel via Intigriti
2026-05-19 Reported to the iwd maintainers
2026-05-21 Patches sent to iwd maintainers
2026-05-22 Intigriti closed the submission as out of scope
2026-05-28 Maintainers confirmed all four findings
2026-06-29 Valve PSIRT notified (SteamOS ships iwd as default)
2026-07-21 Notified the maintainers of a 27 July publication date
2026-08-01 This advisory, write-up and PoC code
CVE assignment:
CVE requested with MITRE:
CAN-2026-2051869 finding 1, RRM beacon report stack buffer overflow
CAN-2026-2051870 finding 2, HE Capabilities validator byte offset
CAN-2026-2051871 finding 3, mde_equal() self-comparison
CAN-2026-2051872 finding 4, FTE sub-element uint8_t underflow
Credits: Abhinav Agarwal
Powered by blists - more mailing lists
Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.