CVE-2026-5479: Verified Repro With Script Download
CVE-2026-5479: wolfSSL: EVP ChaCha20-Poly1305 decryption returns plaintext without verifying authentication tag
CVE-2026-5479 is verified against wolfssl · source. Affected versions: < 5.9.1. Fixed in 5.9.1. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00172.
What Is CVE-2026-5479?
CVE-2026-5479 (CWE-354) is a high-severity vulnerability in wolfSSL's EVP compatibility API where ChaCha20-Poly1305 decryption returns plaintext to the caller without verifying that the Poly1305 authentication tag matches, silently defeating the AEAD's authenticity guarantee. Pruva reproduced it (reproduction REPRO-2026-00172).
CVE-2026-5479 Severity & CVSS Score
CVE-2026-5479 is rated high severity, with a CVSS base score of 8.1 out of 10.
High — serious impact or readily exploitable. Prioritize remediation.
Affected wolfssl Versions
wolfssl · source versions < 5.9.1 are affected.
How to Reproduce CVE-2026-5479
pruva-verify REPRO-2026-00172 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00172/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-5479
Reproduced by Pruva's autonomous agents — 175 tool calls over 13 min. Full root-cause analysis and the complete transcript are below.
Systematic variant attempts against wolfSSL EVP ChaCha20-Poly1305 tag verification fix. Three distinct variants tested: (1) EVP_CipherInit streaming path, (2) modified AAD with correct tag, (3) flipped ciphertext byte with correct tag. None reproduced on fixed version.
How the agent worked
Root Cause and Exploit Chain for CVE-2026-5479
CVE-2026-5479 is an integrity-check bypass (CWE-354) in wolfSSL's EVP compatibility layer for ChaCha20-Poly1305 AEAD decryption. In wolfSSL_EVP_CipherFinal (wolfcrypt/src/evp.c), the decrypt path for WC_CHACHA20_POLY1305_TYPE calls wc_ChaCha20Poly1305_Final() to compute the Poly1305 authentication tag, which overwrites the expected tag previously stored in ctx->authTag by EVP_CIPHER_CTX_ctrl(EVP_CTRL_AEAD_SET_TAG). No comparison between the computed tag and the expected tag is ever performed, so any forged or mismatched tag is silently accepted. This completely nullifies the authenticity guarantee of the AEAD cipher, allowing an active network adversary to supply modified ciphertext that the application will treat as integrity-validated.
- Package/component affected: wolfSSL
libwolfssl, specifically the EVP compatibility API (wolfSSL_EVP_CipherFinal) for ChaCha20-Poly1305 decryption. - Affected versions: wolfSSL
< 5.9.1(last vulnerable release tag:v5.9.0-stable). - Fixed versions:
5.9.1(commit1faddd640). - Risk level: High (CVSS 3.1: 8.1, CVSS 4.0: 7.6).
- Consequences: Any consumer using the standard EVP decrypt pattern (
EVP_DecryptInit_ex→EVP_DecryptUpdate→EVP_CIPHER_CTX_ctrl(SET_TAG)→EVP_DecryptFinal_ex) will accept attacker-modified ciphertext as authentic. This enables message forgery in protocols that rely on ChaCha20-Poly1305 via the wolfSSL EVP API (e.g., TLS record-layer AEAD when used through the EVP abstraction).
Root Cause
The bug is located in wolfcrypt/src/evp.c inside the wolfSSL_EVP_CipherFinal function, specifically in the WC_CHACHA20_POLY1305_TYPE branch (lines ~1502–1515 in v5.9.0-stable).
On the decrypt path (!ctx->enc), the code does:
case WC_CHACHA20_POLY1305_TYPE:
if (wc_ChaCha20Poly1305_Final(&ctx->cipher.chachaPoly,
ctx->authTag) != 0) {
WOLFSSL_MSG("wc_ChaCha20Poly1305_Final failed");
return WOLFSSL_FAILURE;
}
else {
*outl = 0;
return WOLFSSL_SUCCESS;
}
break;
wc_ChaCha20Poly1305_Final() computes the Poly1305 tag and writes it into ctx->authTag, overwriting the expected tag that was set earlier via EVP_CTRL_AEAD_SET_TAG. The code then unconditionally returns WOLFSSL_SUCCESS without ever comparing the computed tag to the expected tag.
The fix (commit 1faddd640edc8195c1fb7cb3904876e434ecab87, PR #10102) mirrors the existing AES-GCM branch: it saves the expected tag before calling _Final(), then uses wc_ChaCha20Poly1305_CheckTag() to verify the computed tag against the saved expected tag on the decrypt path. If the tags do not match, WOLFSSL_FAILURE is returned.
Diff excerpt from the fix:
+ {
+ byte computedTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
+ if (!ctx->enc) {
+ XMEMCPY(computedTag, ctx->authTag, sizeof(computedTag));
+ }
if (wc_ChaCha20Poly1305_Final(&ctx->cipher.chachaPoly,
ctx->authTag) != 0) {
WOLFSSL_MSG("wc_ChaCha20Poly1305_Final failed");
return WOLFSSL_FAILURE;
}
- else {
- *outl = 0;
- return WOLFSSL_SUCCESS;
+ if (!ctx->enc) {
+ int tagErr = wc_ChaCha20Poly1305_CheckTag(computedTag,
+ ctx->authTag);
+ ForceZero(computedTag, sizeof(computedTag));
+ if (tagErr != 0) {
+ WOLFSSL_MSG("ChaCha20-Poly1305 tag mismatch");
+ return WOLFSSL_FAILURE;
+ }
}
- break;
+ *outl = 0;
+ return WOLFSSL_SUCCESS;
+ }
+ break;
Reproduction Steps
Run
repro/reproduction_steps.sh. This self-contained script:- Builds vulnerable wolfSSL (
v5.9.0-stable) into/tmp/wolfssl-vulnand fixed wolfSSL (v5.9.1-stable) into/tmp/wolfssl-fixed(or uses pre-built installations if present). - Compiles
repro/aead_tag_check.cagainst both libraries. - Executes the test harness against each build.
- Generates
logs/runtime_manifest.jsonwith concrete API return values. - Prints a verdict based on observable cryptographic behavior.
- Builds vulnerable wolfSSL (
What the script does:
- Encrypts a known plaintext under ChaCha20-Poly1305 with a fixed key/nonce.
- Performs three tamper attacks on the ciphertext/tag pair:
- Replaces the 16-byte authentication tag with all zeros.
- Replaces the tag with a random bad tag (
0xABrepeated). - Flips the first byte of the ciphertext while keeping the original tag.
- Decrypts each tampered pair using the standard EVP API (
EVP_DecryptInit_ex→EVP_DecryptUpdate→EVP_CIPHER_CTX_ctrl(SET_TAG)→EVP_DecryptFinal_ex). - Captures the return value of
EVP_DecryptFinal_exand the decrypted bytes.
Expected evidence:
- Vulnerable build:
EVP_DecryptFinal_exreturns1(success) for all three tampered inputs, and the decrypted plaintext is delivered to the caller (either matching the original plaintext for forged tags, or producing modified bytes for flipped ciphertext). - Fixed build:
EVP_DecryptFinal_exreturns0(failure) for all three tampered inputs, correctly rejecting the forgeries.
- Vulnerable build:
Evidence
Vulnerable run log:
logs/vulnerable_output.txt- Excerpt (Test 1 — zeroed tag):
EVP_DecryptUpdate ret=1, EVP_DecryptFinal_ex ret=1 Decrypted: 54686520717569636b2062726f776e20666f78206a756d7073206f7665720000 Plaintext match: YES VULNERABLE: YES (Final succeeded with forged tag) - Excerpt (Test 3 — flipped ciphertext byte):
EVP_DecryptUpdate ret=1, EVP_DecryptFinal_ex ret=1 Decrypted: ab686520717569636b2062726f776e20666f78206a756d7073206f7665720000 Plaintext match: NO VULNERABLE: YES (Final succeeded with modified ciphertext)
- Excerpt (Test 1 — zeroed tag):
Fixed run log:
logs/fixed_output.txt- Excerpt (Test 1 — zeroed tag):
EVP_DecryptUpdate ret=1, EVP_DecryptFinal_ex ret=0 Decrypted: 54686520717569636b2062726f776e20666f78206a756d7073206f7665720000 Plaintext match: YES VULNERABLE: NO (Final rejected forged tag)
- Excerpt (Test 1 — zeroed tag):
Runtime manifest:
logs/runtime_manifest.json- Contains structured JSON with the exact
EVP_DecryptFinal_exreturn codes for each tamper strategy on both the vulnerable and fixed builds.
- Contains structured JSON with the exact
Environment:
- Host:
x86_64-pc-linux-gnu - Compiler:
gcc - wolfSSL configure flags:
--enable-chacha --enable-poly1305 --enable-aesgcm --enable-opensslextra --disable-shared --enable-static --enable-debug CFLAGS='-g -O0'
- Host:
Recommendations / Next Steps
- Upgrade immediately to wolfSSL
5.9.1or later, which contains commit1faddd640. - Verify tag-check coverage in any custom fork or wrapper around
wolfSSL_EVP_CipherFinalfor ChaCha20-Poly1305; the missingwc_ChaCha20Poly1305_CheckTag()call is the root cause. - Regression testing: Add the exact tamper strategies from this reproduction (zeroed tag, random tag, flipped ciphertext byte) to the project's EVP cipher unit tests. The upstream fix already includes a negative test for an all-zero forged tag.
- Audit other AEAD paths: Review
wolfSSL_EVP_CipherFinalfor any other AEAD ciphers that might lack tag verification. The AES-GCM branch already had the correct pattern; ChaCha20-Poly1305 was the outlier.
Additional Notes
- Idempotency:
repro/reproduction_steps.shwas executed twice consecutively on the same host and produced identical results (exit code 0,VERDICT: CONFIRMED). - Edge cases: The reproduction uses the standard
EVP_DecryptInit_ex+EVP_DecryptUpdate+EVP_DecryptFinal_expattern. The streamingEVP_CipherUpdate/EVP_CipherFinalequivalents are also affected because they route through the samewolfSSL_EVP_CipherFinalcode path. - Limitations: The reproduction requires building wolfSSL from source with
--enable-chacha --enable-poly1305 --enable-opensslextra. On systems without a C compiler or git, the script will exit with a descriptive error rather than a false positive.
Variant Analysis & Alternative Triggers for CVE-2026-5479
No bypass or alternate trigger of CVE-2026-5479 was found in wolfSSL 5.9.1-stable. The original fix (commit 1f363f3adceba9d1478230ede476a37b0dcdef24, PR #10102) adds explicit Poly1305 tag comparison inside wolfSSL_EVP_CipherFinal for WC_CHACHA20_POLY1305_TYPE. Three distinct variant attempts were systematically tested against both the vulnerable (v5.9.0-stable, 922d04b3568c6428a9fb905ddee3ef5a68db3108) and fixed (v5.9.1-stable) versions. All variant attempts reproduced on the vulnerable version and were correctly rejected on the fixed version, confirming the fix is complete.
Fix Coverage / Assumptions
The fix relies on the invariant that all ChaCha20-Poly1305 decryption flows through the EVP compatibility layer must pass through wolfSSL_EVP_CipherFinal (or its macro aliases EVP_DecryptFinal_ex, EVP_CipherFinal, EVP_CipherFinal_ex). The fix explicitly covers the WC_CHACHA20_POLY1305_TYPE branch in wolfcrypt/src/evp.c (~line 1501).
The fix does not cover:
- Plaintext already emitted by
EVP_DecryptUpdate(consistent with OpenSSL EVP semantics; caller must discard onFinalfailure). - The low-level
wc_ChaCha20Poly1305_Final()function itself, which still only computes the tag. However, all other non-test callers of that function (wc_ChaCha20Poly1305_Decrypt,wc_ChaCha20Poly1305_Encrypt) already handle verification or generation correctly. wolfSSL_EVP_Cipher, which does not support ChaCha20-Poly1305 at all (returnsWOLFSSL_FATAL_ERROR).
Variant / Alternate Trigger Attempts
Three distinct variant attempts were encoded in vuln_variant/variant_test.c and executed via vuln_variant/reproduction_steps.sh:
Variant 1 —
EVP_CipherInit+EVP_CipherUpdate+EVP_CipherFinalpath (QUIC-like streaming API)- Entry point:
wolfSSL_EVP_CipherInit(ctx, EVP_chacha20_poly1305(), key, iv, 0)→wolfSSL_EVP_CipherUpdate(AAD) →wolfSSL_EVP_CipherUpdate(ciphertext) →wolfSSL_EVP_CipherFinal - This mimics the QUIC AEAD decrypt path (
src/quic.c), which uses the genericCipherAPI rather than theDecryptAPI. - Result: Vulnerable version accepted bad tag; fixed version rejected it.
- Entry point:
Variant 2 — Modified AAD with correct tag and ciphertext
- Entry point:
EVP_DecryptInit_ex→EVP_DecryptUpdate(modified AAD) →EVP_DecryptUpdate(correct ciphertext) →EVP_DecryptFinal_ex - Tests whether the Poly1305 AAD integrity check can be bypassed.
- Result: Vulnerable version accepted modified AAD; fixed version rejected it.
- Entry point:
Variant 3 — Flipped ciphertext byte with correct tag
- Entry point:
EVP_DecryptInit_ex→EVP_DecryptUpdate(correct AAD) →EVP_DecryptUpdate(one flipped ciphertext byte) →EVP_DecryptFinal_exwith correct expected tag. - Tests whether ciphertext data integrity is enforced.
- Result: Vulnerable version accepted modified ciphertext; fixed version rejected it.
- Entry point:
All three variants reach the same sink (wolfSSL_EVP_CipherFinal in wolfcrypt/src/evp.c), but they represent materially different attacker-controlled inputs (bad tag vs. modified AAD vs. modified ciphertext).
- Package/component: wolfSSL
libwolfssl, EVP compatibility layer (wolfSSL_EVP_CipherFinal). - Affected versions tested: v5.9.0-stable (vulnerable) and v5.9.1-stable (fixed).
- Risk level: The original vulnerability is High (CVSS 3.1: 8.1). No additional risk surface was identified.
- Consequences: On the vulnerable version, all three variants successfully delivered decrypted plaintext to the caller despite forged or modified inputs, breaking AEAD authenticity guarantees. On the fixed version, all variants were correctly blocked.
Root Cause
The same root cause underlies all variant attempts: in wolfSSL_EVP_CipherFinal, the WC_CHACHA20_POLY1305_TYPE decrypt path called wc_ChaCha20Poly1305_Final(&ctx->cipher.chachaPoly, ctx->authTag). This function computes the Poly1305 tag and stores it into the provided buffer (ctx->authTag), overwriting the expected tag that was previously set by EVP_CIPHER_CTX_ctrl(EVP_CTRL_AEAD_SET_TAG). No subsequent comparison was performed. Because ChaCha20 is a stream cipher, EVP_DecryptUpdate already decrypts ciphertext incrementally; the only integrity check that remained was in Final, and it was missing.
The fix (commit 1d363f3adceba9d1478230ede476a37b0dcdef24) saves the expected tag before wc_ChaCha20Poly1305_Final overwrites it, then compares the saved expected tag with the computed tag using wc_ChaCha20Poly1305_CheckTag(). This closes the gap for all callers of wolfSSL_EVP_CipherFinal, including EVP_DecryptFinal_ex, EVP_CipherFinal, and EVP_CipherFinal_ex (which are all macros aliased to the same function).
Reproduction Steps
- Run
bash vuln_variant/reproduction_steps.sh. - The script compiles
vuln_variant/variant_test.cagainst both pre-built wolfSSL libraries (/tmp/wolfssl-vulnand/tmp/wolfssl-fixed). - It executes three distinct variant attempts on each version.
- The script exits
0only if any variant reproduces on the fixed version (bypass). It exits1when the fixed version correctly rejects all variants.
Expected evidence:
logs/variant_vuln_output.txt: Shows "VULNERABLE: YES" for all three variants (the original bug and its alternate triggers are reproduced).logs/variant_fixed_output.txt: Shows "VULNERABLE: NO" for all three variants (the fix blocks them).
Evidence
- Log files:
logs/variant_vuln_output.txt: Vulnerable version results (all variants succeed).logs/variant_fixed_output.txt: Fixed version results (all variants fail).
- Key excerpts (vulnerable):
--- Variant 1: EVP_CipherInit path with bad tag --- EVP_CipherFinal ret=1 VULNERABLE: YES --- Variant 2: Modified AAD with correct tag --- EVP_DecryptFinal_ex ret=1 VULNERABLE: YES --- Variant 3: Flipped ciphertext byte + correct tag --- EVP_DecryptFinal_ex ret=1 VULNERABLE: YES - Key excerpts (fixed):
--- Variant 1: EVP_CipherInit path with bad tag --- EVP_CipherFinal ret=0 VULNERABLE: NO --- Variant 2: Modified AAD with correct tag --- EVP_DecryptFinal_ex ret=0 VULNERABLE: NO --- Variant 3: Flipped ciphertext byte + correct tag --- EVP_DecryptFinal_ex ret=0 VULNERABLE: NO - Environment: wolfSSL v5.9.0-stable (
922d04b3568c6428a9fb905ddee3ef5a68db3108) vs. v5.9.1-stable (1d363f3adceba9d1478230ede476a37b0dcdef24), built with--enable-chacha --enable-poly1305 --enable-aesgcm --enable-opensslextra --disable-shared --enable-static --enable-debug.
Recommendations / Next Steps
Fix is complete — No additional code changes are required for this specific vulnerability. The patch correctly covers all EVP-layer ChaCha20-Poly1305 decrypt paths.
Hardening suggestion: Consider adding a regression test in
tests/api/test_evp_cipher.cthat explicitly testsEVP_CipherInit+EVP_CipherUpdate+EVP_CipherFinalfor ChaCha20-Poly1305 with a forged tag. The existing test suite (test_evp_cipher.c) already has ChaCha20-Poly1305 tests, but they primarily use theDecryptAPI.Hardening suggestion: Evaluate whether
wolfSSL_EVP_CipherFinalshould zero out already-emitted plaintext buffers on ChaCha20-Poly1305 tag mismatch, similar to whatwc_ChaCha20Poly1305_Decryptdoes. While this is architecturally difficult in the streaming EVP API (plaintext is returned incrementally byUpdate), documenting the expectation that callers must discard output onFinalfailure would improve API safety.Security posture: The
/* TODO: Chacha??? */comment inwolfSSL_EVP_Ciphershould be resolved. Either implement ChaCha20-Poly1305 support in the one-shotEVP_CipherAPI (with proper tag verification), or explicitly document that it is unsupported. Leaving a TODO on an AEAD cipher path is a latent risk.
Additional Notes
- Idempotency: The reproduction script was run twice consecutively and produced identical results each time.
- No bypass found: No alternate entry point, encoding, or API wrapper was found that bypasses the fix on v5.9.1-stable.
- Trust boundary: All variant attempts cross the same trust boundary as the original vulnerability (attacker-controlled ciphertext/tag delivered to the wolfSSL EVP decrypt API over a network or IPC channel).
CVE-2026-5479 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
Unknown error
Unknown error
Artifacts and Evidence for CVE-2026-5479
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-5479
Upgrade wolfssl · source to 5.9.1 or later.
FAQ: CVE-2026-5479
How does the CVE-2026-5479 tag-bypass attack work?
Which wolfSSL versions are affected by CVE-2026-5479, and where is it fixed?
How severe is CVE-2026-5479?
How can I reproduce CVE-2026-5479?
References for CVE-2026-5479
Authoritative sources for CVE-2026-5479 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.