ANT-2026-QRYCJNAP · dnsmasq
denial-of-service medium
Severity Claude high · Security research firm high · Maintainer medium
Discovered by an unreleased Anthropic model
Anthropic's analysis, sealed at approval. Disclosure to the maintainer was performed by Trail of Bits.
ANT-2026-QRYCJNAP: DNSSEC NSEC typemap infinite loop hangs daemon
In dnsmasq's DNSSEC validator, prove_non_existence_nsec() (dnssec.c:1340-1356) and the matching NSEC3 loop (1507-1523) iterate NSEC type-bitmap windows but decrement rdlen and advance p by only p[1] (bitmap length) rather than p[1]+2 (header + bitmap). When an attacker supplies a two-byte window block [window#][0x00] with window# != (qtype>>8), the bounds check passes and the loop state never changes, yielding an infinite loop. The attacker controls the NSEC RDATA bytes; a valid RRSIG over malformed bytes is accepted (line 2313 path), and the wildcard path at line 2275 reaches the loop before NSEC signatures are even verified. Because dnsmasq is single-threaded with no per-handler timeout, one hostile reply permanently hangs DNS, DHCP, TFTP, and RA services until the process is killed.
Target
Project: dnsmasq
Version: commit b95d5a25777cc8910d2c2a921c68c778a3b30498 (still present at HEAD)
Location: src/dnssec.c:1354
Technical Details
Built a unit harness that #includes dnssec.c and calls the static prove_non_existence_nsec() with a crafted NSEC RR whose type-bitmap window has window#=1 and bitmap-length=0 (p[1]==0), with the NSEC owner name equal to the queried name so the rc==0 branch is taken. The function never returned; a 3-second SIGALRM watchdog fired, proving the infinite loop. Root cause confirmed at src/dnssec.c:1354-1355: the loop advances by p[1] bytes instead of p[1]+2, so a zero-length bitmap leaves rdlen and p unchanged forever. Identical pattern present in the NSEC3 path. Reaching this in production requires an attacker who controls a DNSSEC-signed zone (or can forge a validly-signed NSEC), which is plausible for any domain owner — a single hostile signed reply hangs the daemon's single-threaded resolver loop.
Crash signature: Hang / 100% CPU in prove_non_existence_nsec typemap loop; 3s SIGALRM watchdog fired (no return)
Reproduction
- Register a domain and provision a valid DNSSEC chain (DS in parent, signed DNSKEY).
- Configure the authoritative server to answer
nx.evil.example AAAAwith a NODATA response whose authority section contains an NSEC record owner==qname and type-bitmap RDATA0xFF 0x00, plus a valid RRSIG over it. - Induce any client behind the target dnsmasq to resolve
nx.evil.example(e.g., a webpage embedding the hostname). - dnsmasq forwards, receives the reply, validates the RRSIG, and calls prove_non_existence() at dnssec.c:2313 for the unanswered question.
- prove_non_existence_nsec() matches owner==qname (rc==0 at line 1312) and enters the typemap loop with p[0]=0xFF, p[1]=0x00, rdlen=2; the loop never advances.
- The single-threaded event loop is blocked; the process spins at 100% CPU until killed.
[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-QRYCJNAP.
Reference: ANT-2026-QRYCJNAP
Anthropic CVD Policy: https://www.anthropic.com/coordinated-vulnerability-disclosure
Triage and disclosure were performed by Trail of Bits.
- Verdict
- true positive
- Severity
- high
Dates from discovery through public reveal.
- 2026-04-08 Reported to tracker
- 2026-04-21 Maintainer acknowledged
- 2026-05-07 Sent to maintainer
- 2026-05-09 Patch released
- 2026-08-17 Publicly revealed
SHA-3-512 hash:
aa75ba64f155bef4fe2bf47909666cea6dbecd39cfa0804362552cd6bb47eab70613db0438ce271d146d51e68e26b89bfb8ec8c1c8caf89093405a2f5e6f8585
Committed 2026-05-07 00:02 PT
Revealed 2026-08-17 10:47 PT
Verify (download preimage.json)
Show preimage JSON
{
"ant_id": "ANT-2026-QRYCJNAP",
"bug_class": "Denial-of-service",
"claude_severity": "high",
"commit_sha": null,
"created_at": "2026-04-09T05:32:53+00:00",
"description": "In dnsmasq's DNSSEC validator, prove_non_existence_nsec() (dnssec.c:1340-1356) and the matching NSEC3 loop (1507-1523) iterate NSEC type-bitmap windows but decrement rdlen and advance p by only p[1] (bitmap length) rather than p[1]+2 (header + bitmap). When an attacker supplies a two-byte window block [window#][0x00] with window# != (qtype>>8), the bounds check passes and the loop state never changes, yielding an infinite loop. The attacker controls the NSEC RDATA bytes; a valid RRSIG over malformed bytes is accepted (line 2313 path), and the wildcard path at line 2275 reaches the loop before NSEC signatures are even verified. Because dnsmasq is single-threaded with no per-handler timeout, one hostile reply permanently hangs DNS, DHCP, TFTP, and RA services until the process is killed.",
"discovered_at": null,
"location": "src/dnssec.c:1354",
"poc_sha256": null,
"preimage_version": 1,
"project": "dnsmasq",
"reproduction": [
"1. Register a domain and provision a valid DNSSEC chain (DS in parent, signed DNSKEY).",
"2. Configure the authoritative server to answer `nx.evil.example AAAA` with a NODATA response whose authority section contains an NSEC record owner==qname and type-bitmap RDATA `0xFF 0x00`, plus a valid RRSIG over it.",
"3. Induce any client behind the target dnsmasq to resolve `nx.evil.example` (e.g., a webpage embedding the hostname).",
"4. dnsmasq forwards, receives the reply, validates the RRSIG, and calls prove_non_existence() at dnssec.c:2313 for the unanswered question.",
"5. prove_non_existence_nsec() matches owner==qname (rc==0 at line 1312) and enters the typemap loop with p[0]=0xFF, p[1]=0x00, rdlen=2; the loop never advances.",
"6. The single-threaded event loop is blocked; the process spins at 100% CPU until killed."
],
"technical_details": "Built a unit harness that #includes dnssec.c and calls the static prove_non_existence_nsec() with a crafted NSEC RR whose type-bitmap window has window#=1 and bitmap-length=0 (p[1]==0), with the NSEC owner name equal to the queried name so the rc==0 branch is taken. The function never returned; a 3-second SIGALRM watchdog fired, proving the infinite loop. Root cause confirmed at src/dnssec.c:1354-1355: the loop advances by p[1] bytes instead of p[1]+2, so a zero-length bitmap leaves rdlen and p unchanged forever. Identical pattern present in the NSEC3 path. Reaching this in production requires an attacker who controls a DNSSEC-signed zone (or can forge a validly-signed NSEC), which is plausible for any domain owner — a single hostile signed reply hangs the daemon's single-threaded resolver loop.",
"title": "DNSSEC NSEC typemap infinite loop hangs daemon",
"vendor_severity": "high"
}