ANT-2026-6615Y595 · wolfSSL

heap-buffer-overflow medium

CVE-2026-5448

Severity Claude medium · Security research firm medium · Maintainer -

REPORT

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

ANT-2026-6615Y595: Final Assessment: 2-Byte Heap Overflow in wolfSSL_X509_notAfter / wolfSSL_X509_notBefore

The public getters wolfSSL_X509_notAfter() and wolfSSL_X509_notBefore() at src/x509.c:4418-4449 serialize a certificate date as [type][length][data] into a 32-byte notAfterData/notBeforeData array, but the upstream parser (internal.c:13831-13835) clamps the date length to MAX_DATE_SZ=32 rather than 30, so the memcpy at offset +2 writes up to 34 bytes into a 32-byte field. Because notAfterData is the last field of the heap-allocated WOLFSSL_X509 struct in common builds, the 2 excess bytes spill past the allocation boundary. An attacker supplying a certificate with a 32-byte GeneralizedTime fully controls the two overflowed bytes (date validation is bypassed under verify=0 in the d2i path), which land on the adjacent heap chunk's size/prev_size metadata under glibc ptmalloc. Triggering requires a build with OPENSSL_EXTRA/KEEP_PEER_CERT/SESSION_CERTS (ubiquitous in practice) and an explicit application call to wolfSSL_X509_notAfter() — a WOLFSSL_ABI export used by JSSE but not invoked automatically during the TLS handshake.

Target

Project: wolfSSL
Location: wolfSSL_X509_notAfter() at src/x509.c:4446

Technical Details

ASAN: "WRITE of size 32 at 0x5220000015f8 ... 0 bytes after 5368-byte region" — the XMEMCPY at x509.c:4446 copies notAfter.length (up to 32) bytes starting at &notAfterData[2], leaving only 30 bytes of in-bounds space. The root cause is an off-by-2 mismatch: the parser/copy at internal.c:13832 and CheckDate() at asn.c:20252 cap the date at MAX_DATE_SZ/CTC_DATE_SIZE (32), but neither accounts for the 2-byte type+length prefix that the getter prepends into the same 32-byte buffer. The +2 header was accounted for elsewhere (asn.c:34471 uses MAX_DATE_SIZE+2), indicating an oversight at this sink.

Crash trace:

==403==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x5220000015f8
WRITE of size 32 at 0x5220000015f8 thread T0
    #1 0x... in wolfSSL_X509_notAfter /opt/wolfssl/./src/x509.c:4446:5
0x5220000015f8 is located 0 bytes after 5368-byte region
allocated by thread T0 here:
    #1 0x... in wolfSSL_X509_new_ex /opt/wolfssl/./src/x509.c:6171:27

Reproduction

  1. Craft a DER X.509 certificate whose notAfter (and optionally notBefore) is a 32-byte GeneralizedTime, e.g. '20250101000000.0000000000000000Z', placing the two desired overflow bytes at positions 31-32.
  2. Deliver the certificate to the victim — as a malicious TLS server's peer cert, or as input to any code path calling wolfSSL_X509_d2i() on untrusted DER.
  3. The d2i path parses with verify=0, so CheckDate errors are suppressed and the raw 32-byte date (arbitrary bytes) is stored into x509->notAfter via CopyDecodedToX509.
  4. Victim calls wolfSSL_X509_notAfter(); XMEMCPY writes 32 bytes at notAfterData+2, spilling bytes 31-32 past the 5368-byte WOLFSSL_X509 heap chunk.
  5. With heap grooming, the 2 bytes overwrite the low 16 bits of the next glibc chunk's size field (or adjacent object data on jemalloc/musl), enabling chunk-overlap / size-corruption exploitation on a later free().

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

Suggested Fix

Clamp the copy length at the sink: use min(x509->notAfter.length, CTC_DATE_SIZE - 2) before the XMEMCPY at src/x509.c:4446 (and the mirror for notBefore at :4429). Alternatively, clamp to MAX_DATE_SZ - 2 at the source in internal.c:13824/13832.

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-6615Y595.


Reference: ANT-2026-6615Y595
Anthropic CVD Policy: https://anthropic.com/security/cvd-policy

SECURITY RESEARCH FIRM ANALYSIS

Triage and disclosure were performed by Calif.

Verdict
true positive
Severity
medium
TIMELINE

Dates from discovery through public reveal.

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

SHA-3-512 hash:

bfaf579a04a708cc492bf7ea857d05814b0c5713f6c98f9d79026fe2e6a91382574d2f01df0491068ff7e56f372eaad683f4ab6cc4e3c3894368a6756e4347e6

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-6615Y595",
  "bug_class": "Heap Buffer Overflow",
  "claude_severity": "medium",
  "commit_sha": null,
  "created_at": "2026-05-20T01:20:34+00:00",
  "description": "The public getters wolfSSL_X509_notAfter() and wolfSSL_X509_notBefore() at src/x509.c:4418-4449 serialize a certificate date as [type][length][data] into a 32-byte notAfterData/notBeforeData array, but the upstream parser (internal.c:13831-13835) clamps the date length to MAX_DATE_SZ=32 rather than 30, so the memcpy at offset +2 writes up to 34 bytes into a 32-byte field. Because notAfterData is the last field of the heap-allocated WOLFSSL_X509 struct in common builds, the 2 excess bytes spill past the allocation boundary. An attacker supplying a certificate with a 32-byte GeneralizedTime fully controls the two overflowed bytes (date validation is bypassed under verify=0 in the d2i path), which land on the adjacent heap chunk's size/prev_size metadata under glibc ptmalloc. Triggering requires a build with OPENSSL_EXTRA/KEEP_PEER_CERT/SESSION_CERTS (ubiquitous in practice) and an explicit application call to wolfSSL_X509_notAfter() — a WOLFSSL_ABI export used by JSSE but not invoked automatically during the TLS handshake.",
  "discovered_at": "2026-03-26T00:00:00+00:00",
  "location": "wolfSSL_X509_notAfter() at src/x509.c:4446",
  "poc_sha256": null,
  "preimage_version": 1,
  "project": "wolfSSL",
  "reproduction": [
    "1. Craft a DER X.509 certificate whose notAfter (and optionally notBefore) is a 32-byte GeneralizedTime, e.g. '20250101000000.0000000000000000Z', placing the two desired overflow bytes at positions 31-32.",
    "2. Deliver the certificate to the victim — as a malicious TLS server's peer cert, or as input to any code path calling wolfSSL_X509_d2i() on untrusted DER.",
    "3. The d2i path parses with verify=0, so CheckDate errors are suppressed and the raw 32-byte date (arbitrary bytes) is stored into x509->notAfter via CopyDecodedToX509.",
    "4. Victim calls wolfSSL_X509_notAfter(); XMEMCPY writes 32 bytes at notAfterData+2, spilling bytes 31-32 past the 5368-byte WOLFSSL_X509 heap chunk.",
    "5. With heap grooming, the 2 bytes overwrite the low 16 bits of the next glibc chunk's size field (or adjacent object data on jemalloc/musl), enabling chunk-overlap / size-corruption exploitation on a later free()."
  ],
  "technical_details": "ASAN: \"WRITE of size 32 at 0x5220000015f8 ... 0 bytes after 5368-byte region\" — the XMEMCPY at x509.c:4446 copies notAfter.length (up to 32) bytes starting at &notAfterData[2], leaving only 30 bytes of in-bounds space. The root cause is an off-by-2 mismatch: the parser/copy at internal.c:13832 and CheckDate() at asn.c:20252 cap the date at MAX_DATE_SZ/CTC_DATE_SIZE (32), but neither accounts for the 2-byte type+length prefix that the getter prepends into the same 32-byte buffer. The +2 header was accounted for elsewhere (asn.c:34471 uses MAX_DATE_SIZE+2), indicating an oversight at this sink.",
  "title": "Final Assessment: 2-Byte Heap Overflow in wolfSSL_X509_notAfter / wolfSSL_X509_notBefore",
  "vendor_severity": "medium"
}