ccmp_setkey omits AES key load for software-decrypt-only keys, causing kernel panic on first received frame
Summary
ccmp_setkey (:140): if(k->wk_flags&IEEE80211_KEY_SWENCRYPT) rijndael_set_key gates ONLY on SWENCRYPT(0x0010). ccmp_decap (:266-268): if(k->wk_flags&IEEE80211_KEY_SWDECRYPT)&&!ccmp_decrypt calls ccmp_decrypt when SWDECRYPT(0x0020) set. If key has SWDECRYPT WITHOUT SWENCRYPT (hardware-encrypt/software-decrypt config reachable via driver-flag override ieee80211_crypto.c:386-410) AES key schedule NEVER loaded. ccmp_attach kmallocs M_ZERO (:104) so Nr=0 ek[]/dk[] all zero. ccmp_decrypt->ccmp_init_blocks->rijndael_encrypt with Nr=0: rijndaelEncrypt (rijndael-alg-fst.c:947 #else branch) r=Nr>>1=0 for(;;){--r} never==0 loop never breaks rk+=8 walks past ek[60] then dk[60] then past ccmp_ctx into adjacent kernel heap -> page fault panic. Panic occurs in ccmp_init_blocks BEFORE MIC check so attacker need not forge valid MIC any privacy-bit-set data frame >=32B triggers. Attacker: WiFi peer sends single protected 802.11 data frame to victim BSS/STA whose driver configures CCMP SWDECRYPT-only (hardware TX crypto + host RX decrypt). No auth needed panic during CBC-MAC block construction. Impact: hard kernel panic complete DoS. Confidence likely: SWDECRYPT-only config may be uncommon (most drivers both HW or both SW) but code path reachable by framework. Fix: gate on IEEE80211_KEY_SWCRYPT (SWENCRYPT|SWDECRYPT).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0654 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | gate rijndael_set_key on IEEE80211_KEY_SWCRYPT (SWENCRYPT|SWDECRYPT) | 487 B | view raw |
| VERDICT.md | verdict | full source trace + rijndael Nr=0 runaway-loop argument | 4.5 KB | β raw |
| build.sh | build-log | no-op (source-only) | 158 B | view raw |
| run.sh | run-log | no-op (no WiFi HW); checks fix.diff applies | 598 B | view raw |
| env.txt | environment | guest uname | 365 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 |
| live_reachability_check.txt | reachability-test | Live wifi reachability evidence - no wifi HW | 1.3 KB | view raw |
DF-0654 β ccmp_setkey omits AES key load for software-decrypt-only CCMP keys
Verdict: BUG CONFIRMED REAL by source trace β not runnable on guest (no WiFi hardware); impact ceiling = deterministic kernel panic (DoS)
Bug summary (source-confirmed)
ccmp_setkey() (sys/netproto/802_11/wlan_ccmp/ieee80211_crypto_ccmp.c:130) loads
the AES key schedule only when IEEE80211_KEY_SWENCRYPT (0x0010) is set:
129 static int
130 ccmp_setkey(struct ieee80211_key *k)
...
134 if (k->wk_keylen != (128/NBBY)) { ... return 0; }
140 if (k->wk_flags & IEEE80211_KEY_SWENCRYPT) // <-- gates ONLY on SWENCRYPT
141 rijndael_set_key(&ctx->cc_aes, k->wk_key, k->wk_keylen*NBBY);
142 return 1;
143 }
But ccmp_decap() calls ccmp_decrypt() when IEEE80211_KEY_SWDECRYPT (0x0020)
is set (ieee80211_crypto_ccmp.c:266-268), and ccmp_decrypt() β
ccmp_init_blocks() β rijndael_encrypt() uses the same ctx->cc_aes.
ccmp_attach() (:97-104) allocates the context with M_ZERO, so if a key is
configured SWDECRYPT without SWENCRYPT, ccmp_setkey skips
rijndael_set_key and cc_aes stays zeroed: Nr == 0, ek[]/dk[] all zero.
In rijndaelEncrypt() (sys/crypto/rijndael/rijndael-alg-fst.c:862), with Nr=0:
946 rk += Nr << 2; // rk unchanged
951 r = Nr >> 1; // r = 0
952 for (;;) {
953 ... read rk[4..7]; rk += 8;
979 if (--r == 0) break; // --r => -1, NEVER == 0 -> loop never breaks
...
--r makes r == -1 (never 0), so the loop never terminates; rk += 8 each
iteration walks rk past ek[60], dk[60], out of the ccmp_ctx and into
adjacent kernel heap. The Te0[(s0>>24)] lookups index on out-of-bounds memory
and rk[] reads eventually hit an unmapped page β fatal page fault.
This fault occurs inside ccmp_init_blocks() (CBC-MAC block construction) before
any MIC check, so an attacker need only deliver a single privacy-bit-set 802.11
data frame (>=32 B) to a BSS/STA whose driver configures CCMP SWDECRYPT-only.
Reachability of the SWDECRYPT-without-SWENCRYPT config
ieee80211_crypto.c::ieee80211_crypto_newkey():
- :320-325 β if the hardware lacks the cipher, it sets both SWCRYPT bits
(flags |= IEEE80211_KEY_SWCRYPT), so the AES key IS loaded β NOT the vulnerable case.
- :386-410 β the driver-flag override path: after dev_key_alloc(), if the
driver changed wk_flags (e.g. a driver doing HW-encrypt / SW-decrypt
asymmetric offload), the cipher context is re-attached with the driver's flags.
A driver that clears SWENCRYPT while keeping SWDECRYPT yields the vulnerable
state where ccmp_setkey skips the key load but ccmp_decap still decrypts.
So the buggy state is reachable via the framework's driver-override path; whether it manifests depends on a driver setting SWDECRYPT-without-SWENCRYPT (uncommon but within the framework's design β asymmetric crypto offload).
Reproduction
Not runnable on this guest: there is no WiFi hardware / ieee80211 VAP, so the
CCMP cipher is never attached and ccmp_setkey/ccmp_decap cannot be invoked.
This is a source/logic finding verified by code trace + the mathematically-certain
rijndaelEncrypt Nr=0 runaway-loop argument. (The #else /* !FULL_UNROLL */
branch at rijndael-alg-fst.c:947 is the one compiled by the DragonFly build.)
Fix
Gate the AES key load on IEEE80211_KEY_SWCRYPT (= SWENCRYPT|SWDECRYPT) so the
key schedule is loaded whenever EITHER software path is used (fix.diff):
- if (k->wk_flags & IEEE80211_KEY_SWENCRYPT)
+ if (k->wk_flags & IEEE80211_KEY_SWCRYPT)
rijndael_set_key(&ctx->cc_aes, k->wk_key, k->wk_keylen*NBBY);
Matches the finding proposal (gate on SWCRYPT). IEEE80211_KEY_SWCRYPT is defined
at sys/netproto/802_11/ieee80211_crypto.h:107-108.
Kernel references
sys/netproto/802_11/wlan_ccmp/ieee80211_crypto_ccmp.c:130-143βccmp_setkeysys/netproto/802_11/wlan_ccmp/ieee80211_crypto_ccmp.c:140β gates on SWENCRYPT onlysys/netproto/802_11/wlan_ccmp/ieee80211_crypto_ccmp.c:266-268βccmp_decapdecrypts on SWDECRYPTsys/netproto/802_11/wlan_ccmp/ieee80211_crypto_ccmp.c:104βccmp_attachM_ZERO (Nr=0)sys/crypto/rijndael/rijndael-alg-fst.c:946-981βrijndaelEncryptNr=0 runaway loopsys/netproto/802_11/wlan/ieee80211_crypto.c:320-325,386-410β flag set / driver-override pathssys/netproto/802_11/ieee80211_crypto.h:82,83,107β SWENCRYPT/SWDECRYPT/SWCRYPT
Fix verification
not_testablenot_applicable: no WiFi HW
Confirmed kernel references
- sys/netproto/802_11/wlan/ieee80211_crypto_ccmp.c:140
- sys/netproto/802_11/wlan/ieee80211_crypto_ccmp.c:266
Detail
Exploit chain
none β HW-gated.
Evidence (decisive lines)
ccmp_setkey in kernel: YES ccmp_decap in kernel: YES Requires wlan interface with CCMP key -> needs wifi HW -> absent
PoC changes
Added live_reachability_check.txt.
Verified recommended fix
fix.diff ensures rijndael_set_key is called whenever SWENCRYPT or SWDECRYPT is set. Matches finding proposal.
Verdict
INCONCLUSIVE (HW-gated, source-confirmed). ccmp_setkey (0xffffffff80758790) and ccmp_decap (0xffffffff807589d0) ARE compiled into the kernel. The bug (AES key schedule never initialized when key has SWDECRYPT without SWENCRYPT) is traced line-by-line. BUT: ccmp_setkey/ccmp_decap are called through the ieee80211_crypto key framework, which requires a wlan interface with CCMP encryption configured. No wifi HW on QEMU guest.
No comments yet.