ANT-2026-S6CB3P3P · isc-projects/bind9
stack-buffer-overflow high
Severity Claude high · Security research firm high · Maintainer -
Discovered by Claude Mythos Preview
Anthropic's analysis, sealed at approval. Disclosure to the maintainer was performed by Ada Logics.
ANT-2026-S6CB3P3P: 03-bind9-matchnsec3-stack-bof
In BIND's DNSSEC zone-verification path, match_nsec3() declares a stack buffer cbm[8244] and passes it to dns_nsec_compressbitmap() without a size argument. The compressor iterates up to 256 bitmap windows emitting up to 34 bytes each (2 header + 32 data), for a worst case of 8704 bytes — a 460-byte shortfall. An attacker supplying a DNSSEC-signed zone file for verification controls which RR types appear at the zone apex and thus which bitmap bytes are written; by placing one TYPE record per window at byte index 31, the attacker forces a 444-byte overflow with ~94% attacker-chosen content. The overflow overwrites the stack canary, six saved callee-save registers, the return address, and 368 bytes of the caller's frame. With the default -fstack-protector-strong build the canary check aborts the process (guaranteed DoS); without stack protector it is a control-flow-hijack write primitive.
Target
Project: bind9
Version: 9.21.21-dev (main branch, March 2026); any commit with cbm[8244] at zoneverify.c:~450
Location: lib/dns/zoneverify.c:468, match_nsec3()
Technical Details
ASAN: "stack-buffer-overflow ... WRITE of size 32 at 0x... in __interceptor_memmove ... [5472, 13716) 'cbm' (line 450) <== Memory access at offset 13716 overflows this variable" (13716 − 5472 = 8244, confirming the buffer size). The root cause is a sizing miscalculation: the author apparently knew the "compressed" NSEC3 wire format could exceed the 8192-byte raw bitmap (hence 8244 > 8192) but underestimated the growth — the per-window 2-byte framing header means dense bitmaps grow rather than shrink, and the true maximum is 256 × 34 = 8704 bytes. dns_nsec_compressbitmap() takes no output-buffer size and writes unconditionally; the bug is the caller's undersized buffer, not the writer.
Crash trace (truncated — full trace in attached crash.log):
==320560==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffb2503cb4 at pc 0x7f86d3e16c21 bp 0x7fffb2500670 sp 0x7fffb24ffe20
WRITE of size 32 at 0x7fffb2503cb4 thread T0
#0 0x7f86d3e16c20 in __interceptor_memmove ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:810
#1 0x7f86d3922c15 in memmove /usr/include/x86_64-linux-gnu/bits/string_fortified.h:36
#2 0x7f86d3922c15 in dns_nsec_compressbitmap ../lib/dns/nsec.c:87
#3 0x7f86d3be6ed3 in match_nsec3 ../lib/dns/zoneverify.c:468
#4 0x7f86d3be6ed3 in verifynsec3 ../lib/dns/zoneverify.c:736
#5 0x7f86d3be77e6 in verifynsec3s ../lib/dns/zoneverify.c:765
#6 0x7f86d3be99fb in verifynode ../lib/dns/zoneverify.c:957
#7 0x7f86d3bee323 in verify_nodes ../lib/dns/zoneverify.c:1761
[... 6 more frames — full trace in crash.log]
Reproduction
- Construct an unsigned zone containing one TYPE RR per NSEC3 bitmap window (256 records), each chosen so the type's bit lands in byte index 31 of its window (e.g., TYPE(W*256+248) for window W)
- Sign the zone with NSEC3: dnssec-signzone -3 - -A -o example.com db.example.com
- Deliver the resulting db.example.com.signed to the target and cause dnssec-verify to process it
- verify_nodes → verifynode → verifynsec3s → verifynsec3 → match_nsec3 populates types[8192] with all 256 windows non-empty at octet 31
- match_nsec3 calls dns_nsec_compressbitmap(cbm, types, maxtype); the compressor emits 255×34 + 1×18 = 8688 bytes into the 8244-byte cbm[] stack buffer
- The 444-byte overflow overwrites the stack canary (rsp+0x2b48), six saved registers (rbx, rbp, r12–r15 at rsp+0x2b58), the return address (rsp+0x2b88), and 368 bytes of the caller's frame
[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-S6CB3P3P.
Reference: ANT-2026-S6CB3P3P
Anthropic CVD Policy: https://www.anthropic.com/coordinated-vulnerability-disclosure
Triage and disclosure were performed by Ada Logics.
- Verdict
- true positive
- Severity
- high
The change that resolved this finding.
diff --git a/lib/dns/include/dns/nsec.h b/lib/dns/include/dns/nsec.h
index eda6214bb16..ebb1c65a438 100644
--- a/lib/dns/include/dns/nsec.h
+++ b/lib/dns/include/dns/nsec.h
@@ -21,7 +21,12 @@
#include <dns/name.h>
#include <dns/types.h>
-#define DNS_NSEC_BUFFERSIZE (DNS_NAME_MAXWIRE + 8192 + 512)
+/*
+ * max compressed bitmap size:
+ * 256 windows * (window number + window length + bitmap (max 256 bits))
+ */
+#define DNS_NSEC_MAXCBMSIZE (256 * ((256 / 8) + 2))
+#define DNS_NSEC_BUFFERSIZE (DNS_NAME_MAXWIRE + DNS_NSEC_MAXCBMSIZE)
isc_result_t
dns_nsec_buildrdata(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node,
diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c
index 70209432a2c..70e21b4270d 100644
--- a/lib/dns/zoneverify.c
+++ b/lib/dns/zoneverify.c
@@ -447,7 +447,7 @@ match_nsec3(const vctx_t *vctx, const dns_name_t *name,
const unsigned char types[8192], unsigned int maxtype,
const unsigned char *rawhash, size_t rhsize,
isc_result_t *vresult) {
- unsigned char cbm[8244];
+ unsigned char cbm[DNS_NSEC_MAXCBMSIZE];
char namebuf[DNS_NAME_FORMATSIZE];
dns_rdata_nsec3_t nsec3;
isc_result_t result;https://github.com/isc-projects/bind9/commit/e43e4bd20a99860e79294afbcc9b75f01ff28c26
Dates from discovery through public reveal.
- 2026-03-26 Patch released
- 2026-03-31 Reported to tracker
- 2026-05-07 Sent to maintainer
- 2026-05-07 Maintainer acknowledged
- 2026-07-08 Publicly revealed
SHA-3-512 hash:
1a81a8c68555b513954cb7daffe6702584f613cf3878faeab4b7f4b4a9b5b80c6b93282ab289d3af24a84df046c42c0e673f533d97f7a509b4a54285a4b828ee
Committed 2026-05-07 07:53 PT
Revealed 2026-07-08 15:58 PT
Verify (download preimage.json)
Show preimage JSON
{
"ant_id": "ANT-2026-S6CB3P3P",
"bug_class": "Stack Buffer Overflow",
"claude_severity": "high",
"commit_sha": null,
"created_at": "2026-04-01T02:25:22+00:00",
"description": "In BIND's DNSSEC zone-verification path, match_nsec3() declares a stack buffer cbm[8244] and passes it to dns_nsec_compressbitmap() without a size argument. The compressor iterates up to 256 bitmap windows emitting up to 34 bytes each (2 header + 32 data), for a worst case of 8704 bytes — a 460-byte shortfall. An attacker supplying a DNSSEC-signed zone file for verification controls which RR types appear at the zone apex and thus which bitmap bytes are written; by placing one TYPE<n> record per window at byte index 31, the attacker forces a 444-byte overflow with ~94% attacker-chosen content. The overflow overwrites the stack canary, six saved callee-save registers, the return address, and 368 bytes of the caller's frame. With the default -fstack-protector-strong build the canary check aborts the process (guaranteed DoS); without stack protector it is a control-flow-hijack write primitive.",
"discovered_at": null,
"location": "lib/dns/zoneverify.c:468, match_nsec3()",
"poc_sha256": null,
"preimage_version": 1,
"project": "bind9",
"reproduction": [
"1. Construct an unsigned zone containing one TYPE<n> RR per NSEC3 bitmap window (256 records), each chosen so the type's bit lands in byte index 31 of its window (e.g., TYPE(W*256+248) for window W)",
"2. Sign the zone with NSEC3: dnssec-signzone -3 - -A -o example.com db.example.com",
"3. Deliver the resulting db.example.com.signed to the target and cause dnssec-verify to process it",
"4. verify_nodes → verifynode → verifynsec3s → verifynsec3 → match_nsec3 populates types[8192] with all 256 windows non-empty at octet 31",
"5. match_nsec3 calls dns_nsec_compressbitmap(cbm, types, maxtype); the compressor emits 255×34 + 1×18 = 8688 bytes into the 8244-byte cbm[] stack buffer",
"6. The 444-byte overflow overwrites the stack canary (rsp+0x2b48), six saved registers (rbx, rbp, r12–r15 at rsp+0x2b58), the return address (rsp+0x2b88), and 368 bytes of the caller's frame"
],
"technical_details": "ASAN: \"stack-buffer-overflow ... WRITE of size 32 at 0x... in __interceptor_memmove ... [5472, 13716) 'cbm' (line 450) <== Memory access at offset 13716 overflows this variable\" (13716 − 5472 = 8244, confirming the buffer size). The root cause is a sizing miscalculation: the author apparently knew the \"compressed\" NSEC3 wire format could exceed the 8192-byte raw bitmap (hence 8244 > 8192) but underestimated the growth — the per-window 2-byte framing header means dense bitmaps grow rather than shrink, and the true maximum is 256 × 34 = 8704 bytes. dns_nsec_compressbitmap() takes no output-buffer size and writes unconditionally; the bug is the caller's undersized buffer, not the writer.",
"title": "03-bind9-matchnsec3-stack-bof",
"vendor_severity": "high"
}