Like a bank vault whose combination lock only checks half the digits, this NEON codepath lets an attacker brute-force the rest
CVE-2026-6330 is an implementation flaw in wolfSSL's hand-optimized ARM64 NEON assembly for ML-KEM (FIPS 203, formerly Kyber) decapsulation. An ins instruction was used where ext was intended, causing the constant-time ciphertext comparison in the Fujisaki-Okamoto re-encryption check to evaluate only ~784 of the 1,568 bytes of an ML-KEM-1024 ciphertext. The unchecked bytes encode compressed decryption noise that is a linear function of the secret key. An attacker who can submit chosen ciphertexts to a decapsulation oracle — e.g. a static ML-KEM key used in HPKE or KEMTLS — can recover 98.5% of the 2,048 secret coefficients with ~600 chosen ciphertexts and a total query budget of 10⁵–10⁶ decapsulations. Affected versions span wolfSSL 5.7.4 through 5.9.1 on ARM64 with NEON enabled; fixed in 5.9.2 (released June 23, 2026). Ephemeral TLS 1.3 hybrid handshakes, which generate a fresh ML-KEM key per session, are NOT vulnerable to key recovery — only to a distinguishing attack.
The vendor rated this MEDIUM 6.5 with a CVSS vector of AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N. That vector is objectively wrong: C:L/I:L for a vulnerability that enables full private-key recovery is indefensible. The corrected confidentiality impact is C:H at minimum — the attacker recovers the long-term secret key itself, not a partial data leak. wolfSSL likely scored this against the most common deployment pattern (ephemeral TLS 1.3, where key recovery doesn't apply), which is a defensible framing for *population-level* risk but badly misleads anyone running static ML-KEM keys in HPKE, KEMTLS, or pinned-key embedded deployments. The NEON bug was independently discovered by Nicholas Carlini of Anthropic; the related AVX2 bug (CVE-2026-10097) was found by Bhabani Sankar Das (@007bsd), who released full end-to-end key recovery code. This is not theoretical — the exploit works on shipped binaries.
4 steps from start to impact.
Identify a wolfSSL ML-KEM static-key endpoint on ARM64
- Target runs wolfSSL 5.7.4–5.9.1 on ARM64 with NEON
- Target uses a static (non-ephemeral) ML-KEM key
- Attacker can reach the decapsulation endpoint over the network
- ML-KEM adoption is still early — most wolfSSL deployments use classical X25519/ECDH
- Static ML-KEM key usage is rarer than ephemeral; TLS 1.3 hybrid mode is ephemeral by default
- No Shodan/Censys fingerprinting exists to remotely identify wolfSSL ML-KEM endpoints
Submit chosen ciphertexts to the decapsulation oracle
wc_MlKemDecapsulate() on the static key. A binary search over quantization thresholds for each unchecked coordinate is performed, requiring 10⁵–10⁶ total decapsulations.- Ability to submit arbitrary ciphertexts for decapsulation against the same static key
- Ability to observe whether decapsulation succeeded or produced implicit rejection (plaintext-checking oracle)
- 10⁵–10⁶ decapsulations is a large volume; rate limiting or anomaly detection could flag this
- Some deployments may not expose a clear oracle signal distinguishing successful decapsulation from rejection
- Protocol-level framing may limit ciphertext manipulation (though ML-KEM ciphertexts are opaque blobs)
Solve the linear system via ordinary least squares
github.com/007bsd/ml-kem-key-recovery.- Successful oracle responses from step 2
- Computational resources for OLS regression (trivial on a laptop)
- None — once oracle data is collected, key recovery is deterministic and fast
Recover full ML-KEM private key and decrypt past/future traffic
- Recovered secret coefficients from step 3
- Captured ciphertexts (past or future) encapsulated to the compromised key
- If the deployment rotates keys frequently, past capture window is limited
- If TLS 1.3 forward secrecy is layered on top, past session keys may be protected even if the KEM key is recovered
ext replaces ins) so the full 1,568-byte ciphertext comparison is performed. Per noisgate HIGH remediation SLA, deploy within 180 days — but for any system actively using static ML-KEM keys, treat as emergency and patch within 3 days.--disable-armasm) forces the reference C codepath, which performs the full ciphertext comparison correctly. This trades performance for correctness. Note: the AVX2 codepath (CVE-2026-10097) has a similar but separate bug — if cross-compiling, ensure neither SIMD path is used.- WAF / IPS signatures — ML-KEM ciphertexts are opaque binary blobs inside TLS records; no WAF can distinguish a legitimate ciphertext from a crafted one. The attack operates within the normal protocol flow.
- TLS inspection / decryption proxies — These break the end-to-end KEM handshake and would prevent the attack, but they also prevent PQC from functioning at all. This is not a compensating control; it's disabling the feature.
- Network segmentation alone — While limiting attacker reach to the decapsulation endpoint helps, the attack is purely logical and works over any network path that reaches the service. Segmentation slows the attacker but does not prevent the key recovery once oracle access is obtained.
- FIPS mode — wolfSSL's FIPS 140-3 validated modules (certificates #4718, #5041) do not include ML-KEM. The FIPS codepath is unaffected, but this is because it doesn't offer PQC, not because it mitigates the bug.
The supporting signals.
| In-the-Wild Exploitation | No confirmed in-the-wild exploitation. Not listed in CISA KEV. No GreyNoise or Shadowserver scanning activity observed. The attack requires static ML-KEM keys and sustained oracle access, making opportunistic exploitation unlikely. |
|---|---|
| Proof-of-Concept | Public PoC available. Bhabani Sankar Das (@007bsd) released full end-to-end key recovery code at github.com/007bsd/ml-kem-key-recovery covering both AVX2 (CVE-2026-10097) and NEON (CVE-2026-6330) codepaths. The PoC includes oracle construction, ciphertext generation, and OLS key recovery scripts. A companion academic preprint is at eprint.iacr.org/2026/1682. |
| EPSS | 0.00169 (bottom ~15th percentile). Reflects the narrow exposure surface — ML-KEM adoption is early and the ARM64 NEON intersection is small. EPSS does not capture the catastrophic impact ceiling for affected deployments. |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N (vendor-assigned, 6.5 MEDIUM). The C:L/I:L ratings are objectively wrong for a full private-key recovery — the corrected vector should be at minimum C:H/I:H, yielding a base score of 9.1. The vendor likely scored against ephemeral TLS 1.3 usage where key recovery doesn't apply. |
| Affected Versions | wolfSSL 5.7.4 through 5.9.1 on ARM64 with NEON SIMD. The NEON codepath was introduced in 5.7.4 (late 2024). The related AVX2 bug (CVE-2026-10097) affects 5.7.0–5.9.1 on x86-64. Earlier 5.7.x versions required --enable-experimental for ML-KEM, limiting exposure. |
| Fixed Version | wolfSSL 5.9.2 (released June 23, 2026). Fix commit: ac01707f552c611fbd135cc723b2682b3e7f80f2. PR #10192. Downstream: Debian and Ubuntu have tracking entries (DEBIAN-CVE-2026-6330, UBUNTU-CVE-2026-6330). |
| Scanning / Exposure | No Shodan, Censys, or FOFA fingerprints exist for wolfSSL ML-KEM endpoints. wolfSSL does not expose version strings in TLS handshakes by default. Remote identification requires firmware analysis or SBOM inspection. wolfSSL claims 5+ billion device installations across IoT, automotive, industrial, and government sectors. |
| Researchers | Nicholas Carlini (Anthropic) independently discovered the NEON bug via Project Glaswing. Bhabani Sankar Das (@007bsd) discovered the AVX2 variant and published the PoC and preprint. Carlini previously found CVE-2026-5194 (critical wolfSSL cert verification flaw). |
| Related CVEs | CVE-2026-10097 (AVX2 variant, rated HIGH by wolfSSL) — same class of bug on the x86-64 codepath, skipping the final 32 bytes. Both CVEs share the same root cause: incomplete FO re-encryption comparison in SIMD-optimized assembly. |
| PQC Deployment Context | ML-KEM adoption is early but accelerating under CNSA 2.0 mandates (NSA target: 2030–2033 for national security systems). wolfSSL is the first CNSA 2.0-compliant embedded crypto provider. Static-key use cases (HPKE recipients, KEMTLS, embedded pinned keys) are the highest-risk deployments. Firmware update cycles for embedded devices are measured in quarters to years. |
Sources.
Why this verdict
- Understated CVSS vector: The vendor's
C:L/I:Lis objectively wrong for a full private-key recovery attack. The corrected impact isC:H/I:H— the attacker recovers the long-term secret key itself, enabling decryption of all past and future traffic under that key. This alone warrants an upward correction of +2 to +3 points from the vendor baseline. - Public PoC with demonstrated key recovery: @007bsd's repository provides turnkey end-to-end key recovery code that works on shipped wolfSSL binaries. Recovery of 98.5% of secret coefficients with ~600 chosen ciphertexts is practical and repeatable. This is not a theoretical distinguishing attack — it is a complete cryptographic break of IND-CCA2 security.
- Narrow but non-trivial exposure surface: ML-KEM adoption is early (<5% of wolfSSL deployments), ARM64 NEON narrows further, and the static-key prerequisite eliminates ephemeral TLS 1.3. Each prerequisite compounds: the realistic exposed population is well under 1% of wolfSSL's installed base. This prevents CRITICAL but does not reach LOW.
- Embedded/IoT patch lag extends exposure window: wolfSSL is embedded in firmware across IoT, automotive, and industrial devices where update cycles are measured in quarters or years. Devices that adopted ML-KEM early (CNSA 2.0 pilots, government/defense IoT) may remain vulnerable for extended periods.
- Role multiplier: wolfSSL is deployed across low-value roles (consumer IoT, dev boards) through high-value roles (automotive ECUs, industrial control gateways, government/military embedded systems, TPM-backed key stores). For high-value roles: (1) Government/defense embedded systems adopting ML-KEM under CNSA 2.0 mandates are the most plausible static-key deployments on ARM64 — the chain succeeds here and the blast radius is *key compromise → decryption of classified communications → national security impact*. (2) Industrial/OT gateways using wolfSSL with pinned ML-KEM keys face *device impersonation and traffic decryption*. These roles represent ≥1% of wolfSSL's installed base (wolfSSL explicitly markets to government, automotive, and industrial sectors), establishing a HIGH floor per the deployment-role rule. The chain requires ML-KEM enablement, which is currently rare but growing in exactly these high-value sectors.
Why not higher?
CRITICAL would require either broader exposure or confirmed in-the-wild exploitation. ML-KEM adoption remains early — the vast majority of wolfSSL deployments still use classical X25519/ECDH and are completely unaffected. The static-key prerequisite eliminates the most common use case (ephemeral TLS 1.3 hybrid handshakes). The 10⁵–10⁶ query budget, while feasible, provides a detection window that ephemeral-key deployments never reach. Without evidence that ≥10% of wolfSSL's installed base is running ML-KEM with static keys on ARM64 NEON, the CRITICAL floor is not met.
Why not lower?
MEDIUM would understate the catastrophic impact for affected deployments. This is a full private-key recovery with a public PoC — the most severe class of cryptographic break short of algorithm compromise. wolfSSL is a FIPS-validated library explicitly marketed for government, military, automotive, and industrial use cases where static ML-KEM keys are the most likely deployment pattern. The deployment-role floor (HIGH) applies because these high-value roles represent ≥1% of wolfSSL's installed base and the chain succeeds in them with fleet/national-security-scale impact. EPSS and current exploitation status reflect population-level probability, not impact — they should not override the floor.
Crowdsourced verification payload.
Run this script on each target host (or against firmware images) where wolfSSL may be installed. Execute as any user with read access to the filesystem: bash check_cve_2026_6330.sh. The script checks for wolfSSL library presence, version, ARM64 NEON enablement, and ML-KEM configuration.
#!/usr/bin/env bash
# CVE-2026-6330 Detection Script
# Checks for vulnerable wolfSSL ML-KEM NEON codepath
# Output: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 2=VULNERABLE, 0=PATCHED, 1=UNKNOWN
set -euo pipefail
RESULT="UNKNOWN"
ARCH=$(uname -m 2>/dev/null || echo "unknown")
# Locate wolfSSL library
WOLFSSL_LIB=""
for candidate in \
/usr/lib/libwolfssl.so \
/usr/local/lib/libwolfssl.so \
/usr/lib/aarch64-linux-gnu/libwolfssl.so \
/usr/lib64/libwolfssl.so \
/opt/wolfssl/lib/libwolfssl.so; do
if [ -f "$candidate" ]; then
WOLFSSL_LIB="$candidate"
break
fi
done
# Also check pkg-config
if [ -z "$WOLFSSL_LIB" ] && command -v pkg-config &>/dev/null; then
WOLFSSL_LIB=$(pkg-config --variable=libdir wolfssl 2>/dev/null || true)
if [ -n "$WOLFSSL_LIB" ]; then
WOLFSSL_LIB="${WOLFSSL_LIB}/libwolfssl.so"
[ ! -f "$WOLFSSL_LIB" ] && WOLFSSL_LIB=""
fi
fi
if [ -z "$WOLFSSL_LIB" ]; then
echo "[INFO] wolfSSL library not found on this host."
echo "UNKNOWN"
exit 1
fi
echo "[INFO] Found wolfSSL at: $WOLFSSL_LIB"
# Extract version string from library
VERSION=$(strings "$WOLFSSL_LIB" 2>/dev/null | grep -oP 'wolfSSL \K[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true)
if [ -z "$VERSION" ]; then
# Try wolfssl-config
if command -v wolfssl-config &>/dev/null; then
VERSION=$(wolfssl-config --version 2>/dev/null || true)
fi
fi
if [ -z "$VERSION" ]; then
echo "[WARN] Could not determine wolfSSL version."
echo "UNKNOWN"
exit 1
fi
echo "[INFO] wolfSSL version: $VERSION"
echo "[INFO] Architecture: $ARCH"
# Parse version components
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
# Check if ARM64/aarch64
IS_ARM64=false
if [[ "$ARCH" == "aarch64" || "$ARCH" == "arm64" ]]; then
IS_ARM64=true
fi
# Check NEON symbols in library
HAS_NEON=false
if $IS_ARM64 && strings "$WOLFSSL_LIB" 2>/dev/null | grep -qi 'neon\|mlkem.*neon\|kyber.*neon'; then
HAS_NEON=true
fi
# Version comparison: vulnerable is 5.7.4 <= version <= 5.9.1
is_vulnerable_version() {
if [ "$MAJOR" -lt 5 ]; then return 1; fi
if [ "$MAJOR" -gt 5 ]; then return 1; fi
# MAJOR is 5
if [ "$MINOR" -lt 7 ]; then return 1; fi
if [ "$MINOR" -eq 7 ] && [ "$PATCH" -lt 4 ]; then return 1; fi
if [ "$MINOR" -gt 9 ]; then return 1; fi
if [ "$MINOR" -eq 9 ] && [ "$PATCH" -ge 2 ]; then return 1; fi
return 0
}
if is_vulnerable_version; then
if $IS_ARM64; then
echo "[ALERT] wolfSSL $VERSION on ARM64 — NEON ML-KEM codepath is vulnerable to CVE-2026-6330."
RESULT="VULNERABLE"
else
echo "[INFO] wolfSSL $VERSION is in the vulnerable range but this host is $ARCH (not ARM64)."
echo "[INFO] CVE-2026-6330 (NEON) does not apply. Check CVE-2026-10097 (AVX2) if x86_64."
RESULT="PATCHED"
fi
else
echo "[INFO] wolfSSL $VERSION is outside the vulnerable range (5.7.4–5.9.1)."
RESULT="PATCHED"
fi
echo "$RESULT"
case "$RESULT" in
VULNERABLE) exit 2 ;;
PATCHED) exit 0 ;;
*) exit 1 ;;
esac