ANT-2026-ZZY4987K · wolfssl/wolfssl

integer-overflow high

CVE-2026-5477 GHSA-grqc-3vmg-p68x

Severity Claude high · Security research firm high · Maintainer high

Discovered by Claude Mythos Preview

REPORT

Anthropic's analysis, sealed at approval. Disclosure to the maintainer was performed by Calif.

ANT-2026-ZZY4987K: wolfCrypt wc_CmacUpdate: 32-bit totalSz wraparound enables CMAC forgery on >4 GiB messages

wolfCrypt's streaming CMAC implementation kept the number of bytes processed in a 32-bit counter (totalSz) and used the test totalSz != 0 to decide whether to XOR the running digest into the next block before encrypting it. After 4 GiB of input the counter wraps back to zero, so wc_CmacUpdate() skips the chaining step a second time and silently discards the accumulated CBC-MAC state. As a result, any two messages that are identical from the 4 GiB offset onward produce the same CMAC tag under the same key, allowing an attacker who holds one valid tag for such a message to substitute the first 4 GiB with arbitrary content (CVE-2026-5477, GHSA-grqc-3vmg-p68x). wolfSSL fixed the issue by making the XOR unconditional; the fix is included in wolfSSL 5.9.1.

Target

Project: wolfSSL
Location: wolfcrypt/src/cmac.c:wc_CmacUpdate
Discovery: static analysis — not yet dynamically reproduced

Technical Details

Component. wc_CmacUpdate() in wolfcrypt/src/cmac.c, and the equivalent block-flush path in wc_local_CmacUpdateAes() in wolfcrypt/src/aes.c.

Root cause. When the internal 16-byte buffer is full and more input remains, the update routine chains the buffered block into the CBC-MAC state and encrypts it:

if (cmac->bufferSz == WC_AES_BLOCK_SIZE && inSz != 0) {
    if (cmac->totalSz != 0) {                       /* removed by the fix */
        xorbuf(cmac->buffer, cmac->digest, WC_AES_BLOCK_SIZE);
    }
    wc_AesEncryptDirect(&cmac->aes, cmac->digest, cmac->buffer);
    cmac->totalSz += WC_AES_BLOCK_SIZE;
    ...

The totalSz != 0 guard was meant only to skip the XOR on the very first block, where digest is all zeros and the XOR is a no-op. totalSz is a word32, however, and after 2^28 block flushes (4 GiB of input) it wraps to zero. On the next flush the guard fires again, the XOR with the live digest is skipped, and the block is encrypted on its own: the CBC-MAC chain is restarted and every preceding block stops contributing to the tag.

Reachability and impact. The defect is reached by processing a single message longer than 4 GiB through the streaming CMAC update API. Any two messages sharing a common suffix beyond the 4 GiB mark then produce identical CMAC tags, so an attacker who observes one valid tag for a message longer than 4 GiB can replace the first 4 GiB with arbitrary data without invalidating the tag — a zero-work prefix-substitution forgery. This is an integrity failure (CWE-190, Integer Overflow or Wraparound); the published advisory rates it High (CVSS 3.1 base score 7.5, AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N). Messages of 4 GiB or less are not affected.

Fix. Commit 10953f021 ("CMAC: fix wraparound in streaming update", wolfSSL/wolfssl#10102, merged 2026-04-06) removes the guard in both files so the XOR is performed unconditionally; first-block behaviour is unchanged because wc_InitCmac_ex() zero-initialises digest. The fix is included in wolfSSL 5.9.1. Tracked as CVE-2026-5477 / GHSA-grqc-3vmg-p68x.

Reproduction

This finding was identified by static analysis and has not yet been dynamically reproduced. The Technical Details section above describes the code path; a trigger input is not included.

[No reproducer or sanitizer output attached — request from security-cvd@anthropic.com if needed.]

Acknowledgement

This vulnerability was discovered by Claude, Anthropic's AI assistant, and triaged by the Anthropic security team in collaboration with Anthropic Research. Please direct questions to security-cvd@anthropic.com and reference ANT-2026-ZZY4987K.


Reference: ANT-2026-ZZY4987K
Anthropic CVD Policy: https://www.anthropic.com/coordinated-vulnerability-disclosure

SECURITY RESEARCH FIRM ANALYSIS

Triage and disclosure were performed by Calif.

Verdict
true positive
Severity
high
UPSTREAM FIX

The change that resolved this finding.

diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c
index cb4258e6349..a3b75dbb34b 100644
--- a/wolfcrypt/src/aes.c
+++ b/wolfcrypt/src/aes.c
@@ -16377,9 +16377,7 @@ int wc_local_CmacUpdateAes(struct Cmac *cmac, const byte* in, word32 inSz) {
         in += add;
 
         if (cmac->bufferSz == WC_AES_BLOCK_SIZE && inSz != 0) {
-            if (cmac->totalSz != 0) {
-                xorbuf(cmac->buffer, cmac->digest, WC_AES_BLOCK_SIZE);
-            }
+            xorbuf(cmac->buffer, cmac->digest, WC_AES_BLOCK_SIZE);
             ret = AesEncrypt_preFetchOpt(aes, cmac->buffer,
                                             cmac->digest, &did_prefetches);
             if (ret == 0) {
diff --git a/wolfcrypt/src/cmac.c b/wolfcrypt/src/cmac.c
index 66e45f92477..ba579516d1b 100644
--- a/wolfcrypt/src/cmac.c
+++ b/wolfcrypt/src/cmac.c
@@ -238,9 +238,7 @@ int wc_CmacUpdate(Cmac* cmac, const byte* in, word32 inSz)
             inSz -= add;
 
             if (cmac->bufferSz == WC_AES_BLOCK_SIZE && inSz != 0) {
-                if (cmac->totalSz != 0) {
-                    xorbuf(cmac->buffer, cmac->digest, WC_AES_BLOCK_SIZE);
-                }
+                xorbuf(cmac->buffer, cmac->digest, WC_AES_BLOCK_SIZE);
                 wc_AesEncryptDirect(&cmac->aes, cmac->digest,
                         cmac->buffer);
                 cmac->totalSz += WC_AES_BLOCK_SIZE;

https://github.com/wolfSSL/wolfssl/commit/10953f021

TIMELINE

Dates from discovery through public reveal.

  1. 2026-03-29 Reported to tracker
  2. 2026-04-05 Sent to maintainer
  3. 2026-05-07 Patch released
  4. 2026-05-07 Maintainer acknowledged
  5. 2026-05-20 Publicly revealed
PROVENANCE

SHA-3-512 hash:

23871c9f0508a22245ce5d325672eb271592a851965032d220707bf02393f1648f311b3bbe08069181e62c17d40b03a0254368f0ea65ec6100f839e4c80f1be0

Committed 2026-04-05 16:37 PT

Revealed 2026-05-20 00:40 PT

Verify (download preimage.json)

Show preimage JSON
{
  "ant_id": "ANT-2026-ZZY4987K",
  "bug_class": "integer-overflow",
  "claude_severity": "high",
  "commit_sha": null,
  "created_at": "2026-03-29T20:42:33+00:00",
  "description": null,
  "discovered_at": null,
  "location": null,
  "poc_sha256": null,
  "preimage_version": 1,
  "project": "wolfSSL",
  "reproduction": null,
  "technical_details": null,
  "title": "cmac 32 bit totalsz wraparound prefix substitution forg",
  "vendor_severity": "high"
}