[SECURITY] hostap - PASN responder: missing Authentication frame
Elman Shahbazov
shahbazovelman97 at gmail.com
Tue Aug 18 01:15:11 PDT 2026
length validation causes integer underflow and undefined behavior in
handle_auth_pasn_1() / handle_auth_pasn_3()
Hello,
I would like to responsibly disclose an input-validation / memory-safety
issue discovered in the PASN responder code of hostap (current master,
commit <COMMIT>). The issue was found via coverage-guided fuzzing
(AFL++ 5.02c, afl-clang-fast with ASAN+UBSAN) using the project's own
fuzzing harness tests/fuzzing/pasn-resp.
1. SUMMARY
The PASN responder entry points handle_auth_pasn_1() and
handle_auth_pasn_3() in src/pasn/pasn_responder.c parse the Information
Elements of a received Authentication frame using a length computed as:
len - offsetof(struct ieee80211_mgmt, u.auth.variable) /* = 30 */
WITHOUT first validating that len >= offsetof(struct ieee80211_mgmt,
u.auth.variable). If a frame shorter than 30 octets reaches either
function, the unsigned subtraction wraps to a value close to SIZE_MAX
(CWE-191). The wrapped length is then passed to ieee802_11_parse_elems(),
where the for_each_element() macro at src/common/ieee802_11_common.c:490
evaluates (start + len), i.e. pointer arithmetic that overflows the
address space - undefined behavior (UB), flagged by UBSAN as a runtime
error. In a sanitized build the process aborts; in a production build the
behavior is undefined and the frame is silently treated as "parsed
successfully with zero elements" instead of being rejected, after which
the responder continues processing and even transmits a response frame.
2. AFFECTED CODE
src/pasn/pasn_responder.c, handle_auth_pasn_1() (function begins at line
836; vulnerable call at lines 861-864):
if (ieee802_11_parse_elems(mgmt->u.auth.variable,
len - offsetof(struct ieee80211_mgmt,
u.auth.variable),
&elems, 0) == ParseFailed) {
src/pasn/pasn_responder.c, handle_auth_pasn_3() (function begins at line
1246; same pattern at lines 1259-1262).
Neither function validates len against the fixed part of the
Authentication frame before the call. Both functions are declared in
src/pasn/pasn_common.h (lines 271-276) and are consumed by four
subsystems: hostapd AP (src/ap/ieee802_11.c:4153, 4177), P2P
(src/p2p/p2p.c:7232, 7239), proximity ranging
(src/common/proximity_ranging.c:2523, 2619) and NAN pairing
(src/nan/nan_pairing.c:1130, 1174).
3. ROOT CAUSE
CWE-20 Missing Input Validation
CWE-191 Integer Underflow (unsigned wrap-around of "len - 30")
CWE-475 Undefined Behavior at API boundary (pointer overflow)
The safety contract ("len must cover the fixed Authentication fields")
is not enforced by the callee; every caller happens to re-implement the
check independently (see section 5), which is exactly the pattern that
leads to remotely exploitable bugs the moment one caller forgets it.
4. PROOF OF CONCEPT / REPRODUCTION
PoC input: a single octet 0x61 ('a'). Hex dump:
00000000: 61 a
Reproduction without a fuzzer (deterministic):
git clone https://git.w1.fi/cgit/hostap/
cd hostap/tests/fuzzing/pasn-resp
AFL_USE_ASAN=1 AFL_USE_UBSAN=1 make CC=afl-clang-fast
printf 'a' > /tmp/poc
./pasn-resp /tmp/poc
Observed UBSAN output:
../../../src/common/ieee802_11_common.c:490:2: runtime error:
addition of unsigned offset to 0x7be081fe002e overflowed to
0x7be081fe0011
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior
../../../src/common/ieee802_11_common.c:490:2
Stack trace (UBSAN_OPTIONS=print_stacktrace=1):
#0 __ieee802_11_parse_elems
src/common/ieee802_11_common.c:490
#1 handle_auth_pasn_1
src/pasn/pasn_responder.c:864
#2 LLVMFuzzerTestOneInput
tests/fuzzing/pasn-resp/pasn-resp.c:103
#3 main tests/fuzzing/fuzzer-common.c:52
The same crash was originally found by AFL++ within one minute of
fuzzing (saved crash:
id:000000,sig:06,src:000000,time:58,execs:47,op:(null),pos:0).
5. CALLER AUDIT / REACHABILITY
Current in-tree callers and their validation:
- src/ap/ieee802_11.c (handle_auth, check at line 4241:
"len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)") -> validated
- src/p2p/p2p.c (p2p_handle_pasn_auth:
"len < offsetof(...u.auth.variable)") -> validated
- src/common/proximity_ranging.c (pr_pasn_auth_rx:
"len < offsetof(...u.auth.variable)") -> validated
- src/nan/nan_pairing.c (nan_pairing_process_elems:
"len < offsetof(...u.auth.variable)") -> validated
So in the current tree the missing validation is masked. However:
(a) The functions are a shared, exported API (pasn_common.h) used by
four subsystems; the invariant is enforced nowhere in the API itself.
(b) The inconsistency already exists: handle_auth_pasn() in
src/ap/ieee802_11.c (line 4091) checks only "len < 24"
(IEEE80211_HDRLEN) before performing the same
"len - offsetof(...u.auth.variable)" computation under CONFIG_P2P
(lines ~4100-4108); it is saved only by handle_auth()'s earlier check.
(c) hostapd/wpa_supplicant are among the most forked code bases in
networking; any vendor fork or future caller that omits the check
becomes remotely triggerable over the air (adjacent-network attacker
sending a truncated Authentication frame).
(d) The project's own fuzz harness crashes on this input, i.e. the
parsing entry point does not maintain its own memory-safety
invariant - the exact class of issue upstream has historically fixed
inside the callee.
6. IMPACT ASSESSMENT
In a production (non-sanitized) build the wrapped length makes the
element-iteration terminate immediately, so the truncated frame is
misinterpreted as a valid Authentication frame with zero IEs; the
responder then takes the "No RSNE" path and transmits a response. The
pointer overflow itself is undefined behavior and may be exploited by
optimizing compilers that assume non-wrapping pointer arithmetic. If any
caller without the length check is introduced (or an out-of-tree fork),
the same code becomes a remotely triggerable memory-safety bug. I
therefore recommend treating this as a hardening fix with security
implications rather than a pure refactoring.
7. SUGGESTED FIX
Enforce the contract inside the callee (defense in depth):
--- a/src/pasn/pasn_responder.c
+++ b/src/pasn/pasn_responder.c
@@ -846,6 +846,9 @@ int handle_auth_pasn_1(struct pasn_data *pasn,
u32 i;
+ if (len < offsetof(struct ieee80211_mgmt, u.auth.variable))
+ return -1;
+
if (!groups)
groups = default_groups;
@@ -1255,6 +1258,9 @@ int handle_auth_pasn_3(struct pasn_data *pasn,
u8 hash[SHA512_MAC_LEN];
+ if (len < offsetof(struct ieee80211_mgmt, u.auth.variable))
+ return -1;
+
if (ieee802_11_parse_elems(mgmt->u.auth.variable,
Optionally, for consistency, change the "len < 24" check in
handle_auth_pasn() (src/ap/ieee802_11.c, ~line 4097) to
"len < offsetof(struct ieee80211_mgmt, u.auth.variable)".
8. ENVIRONMENT
- hostap master, commit
- Kali Linux (kernel x86_64), AFL++ 5.02c, clang/LLVM 21
- Build: AFL_USE_ASAN=1 AFL_USE_UBSAN=1 make CC=afl-clang-fast
(tests/fuzzing/pasn-resp)
I am happy to provide the AFL++ crash file, re-test any patch, or
supply further details. Credit for the discovery: independent security
research using AFL++ on Kali Linux.
Best regards,
Elman (independent security researcher)
More information about the Hostap
mailing list