ANT-2026-6615Y595 · wolfSSL
heap-buffer-overflow medium
Severity Claude medium · Security research firm medium · Maintainer -
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 ¬AfterData[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
- 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.
- 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.
- 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.
- Victim calls wolfSSL_X509_notAfter(); XMEMCPY writes 32 bytes at notAfterData+2, spilling bytes 31-32 past the 5368-byte WOLFSSL_X509 heap chunk.
- 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
Triage and disclosure were performed by Calif.
- Verdict
- true positive
- Severity
- medium
Dates from discovery through public reveal.
- 2026-04-05 Sent to maintainer
- 2026-04-08 Patch released
- 2026-05-09 Maintainer acknowledged
- 2026-05-20 Reported to tracker
- 2026-05-20 Publicly revealed
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 ¬AfterData[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"
}