Like a fire alarm that only goes off in buildings wired with two competing smoke-detector brands
CVE-2026-97313 is a remotely-triggerable assertion failure (CWE-617) in ISC BIND 9 recursive resolvers. An unauthenticated attacker sends a query with RD=1 (recursion desired) followed by a second query with RD=0 (no recursion). If the server has two or more RPZ zones where at least one uses the default recursive-only yes and another explicitly sets recursive-only no, the RPZ query-resumption path in rpz_rrset_find() hits an INSIST(st->r.r_type == type) invariant violation at lib/ns/query.c:2845, and named exits. The crash is deterministic once the config precondition is met. Affected versions span BIND 9.16.0–99.18.50, 9.20.0–9.20.29, 9.21.0–9.21.26, plus corresponding -S1 Supported Preview editions. Fixed in 9.20.30 and 9.21.27. Debian Trixie has already backported the fix via DSA-6576-1.
ISC rates this HIGH 7.5 with CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H. The vector is technically correct—the crash *is* network-reachable, unauthenticated, and low-complexity *given* you hit a vulnerable instance. But CVSS cannot encode the configuration prerequisite. RPZ is a minority feature; mixed recursive-only settings across RPZ zones is rarer still. The vendor severity overstates the real-world risk for most BIND operators. This is a configuration-gated DoS, not a universal BIND crash. noisgate downgrades to MEDIUM 5.5.
5 steps from start to impact.
Identify a reachable BIND recursive resolver
dig +recurse @target or Shodan/Censys product:BIND queries reveal candidates. The resolver must be reachable on port 53 UDP/TCP.- Target runs BIND 9.16.0–9.18.50, 9.20.0–9.20.29, or 9.21.0–9.21.26
- Attacker can send DNS queries to the resolver (port 53 reachable)
- Well-managed enterprises restrict recursive DNS to internal clients via ACLs (
allow-recursion), blocking external attackers - Cloud-hosted authoritative DNS (Route 53, Cloudflare, etc.) is not BIND and is unaffected
named -v locallyConfirm RPZ with mixed recursive-only settings
recursive-only values. The attacker cannot remotely confirm this configuration—it is a blind prerequisite. Most RPZ deployments use the default (recursive-only yes) on all zones. Having at least one zone set to recursive-only no is a deliberate, non-default choice made for specific policy reasons (e.g., applying RPZ to non-recursive stub/forwarded queries).- Server has ≥2 RPZ zones configured
- At least one zone has
recursive-only no(non-default) - At least one zone retains
recursive-only yes(default)
- RPZ itself is a minority feature—most BIND deployments are authoritative-only or use simple recursion without RPZ
- Mixed
recursive-onlyacross zones is an unusual, intentional configuration - Attacker has no way to fingerprint this remotely—must spray blindly
grep -c 'recursive-only no' named.confSend RD=1 query to prime the RPZ lookup
RD=1) for a name that triggers RPZ evaluation requiring an asynchronous fetch (e.g., NSDNAME or NSIP policy lookup). This primes the internal state machine (st->r.r_type) for a recursive RPZ resolution path. Standard tools like dig +recurse suffice.- Query must trigger an RPZ policy that requires an async fetch (NSDNAME/NSIP)
- The RPZ zone handling this query must have
recursive-only yes
- Attacker must guess or know a domain name that triggers the right RPZ policy path
- Simple passthru or QNAME-only RPZ policies do not trigger the async path
querylog yes) will capture the sequence; SIEM correlation on rapid RD-flip patternsSend RD=0 query to trigger the assertion failure
RD=0. When fetch_callback() completes and restores recursion settings, the RPZ evaluation resumes with inconsistent eligibility state. The INSIST(st->r.r_type == type) check at query.c:2845 fails, and named calls abort(). The process exits immediately.- The second query arrives while the async fetch from step 3 is still in flight
- Both queries are for the same QNAME so they share the RPZ state
- Timing window is narrow—the async fetch must still be pending
- Network jitter and resolver caching may close the window before the second query arrives
INSIST(st->r.r_type == type) failed followed by exiting (due to assertion failure); monitor for named restarts via systemd journalRepeat to sustain denial of service
named within seconds, the attacker must repeat the two-query sequence continuously to maintain the outage. Each crash-restart cycle takes 5–30 seconds depending on zone load size. A sustained attack requires persistent network access and knowledge of the triggering QNAME.- Persistent network access to the resolver
- Knowledge of a QNAME that triggers the vulnerable RPZ async path
- Rate-limiting on DNS queries (RRL) may throttle the attacker
- Monitoring will quickly detect repeated
namedcrashes and trigger incident response - Failover to a secondary resolver (if configured) limits blast radius
named exit codes or systemd restart-count thresholdsnamed.conf for response-policy blocks. If all RPZ zones can use the default recursive-only yes, remove any explicit recursive-only no directives. This eliminates the vulnerability precondition entirely without upgrading BIND. As a MEDIUM verdict, the noisgate remediation SLA is 365 days, but this config change can be deployed immediately with minimal risk.allow-recursion and allow-query limit recursive resolution to trusted internal subnets only. This prevents external attackers from reaching the vulnerable code path. Does not protect against malicious insiders or compromised internal hosts. Deploy within standard change-management windows.rate-limit in BIND to throttle high-volume query sources. This raises the cost of sustaining a crash loop, as the attacker must overcome rate limits to repeatedly trigger the sequence. Not a full mitigation—a slow, persistent attacker can still trigger the crash—but buys time for detection.named assertion failures (INSIST.*failed) and unexpected restarts. A crash loop is the primary exploitation signal. This does not prevent the crash but ensures rapid incident response. Wire to your SIEM or monitoring stack (Prometheus node_exporter, Zabbix, etc.).- WAF / IPS DNS signatures — The attack uses legitimate DNS query packets with standard flags (RD=1 then RD=0). There is no malformed packet or exploit payload for a signature to match. Deep-packet inspection of DNS will not distinguish this from normal query traffic.
- DNSSEC — DNSSEC validates response authenticity; it has no bearing on server-side assertion failures triggered by query flag combinations. A fully DNSSEC-signed environment is equally vulnerable.
- Upgrading to DNS-over-HTTPS (DoH) or DNS-over-TLS (DoT) — These encrypt the transport but do not change the server-side RPZ evaluation logic. The same RD=1/RD=0 sequence triggers the same assertion failure over encrypted transports.
The supporting signals.
| In-the-wild exploitation | None confirmed. ISC states no active exploits. Not listed in CISA KEV. No GreyNoise or Shadowserver scanning activity observed as of 2026-10-22. |
|---|---|
| Proof-of-concept | Private PoC only. The ISC GitLab issue #6408 references reporter-provided config and test files that produce a deterministic crash. No public PoC on GitHub, ExploitDB, pocindex.io, or nuclei-templates as of 2026-10-22. |
| EPSS score | Not yet scored. CVE disclosed 2026-10-21; FIRST EPSS API returns no data. Expected to be low (<5th percentile) given configuration dependency and DoS-only impact. |
| KEV status | Not listed. No CISA KEV entry. No federal agency deadline. |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H — Network-reachable, no auth, low complexity per spec. The AC:L is technically correct (the crash is deterministic *given* the config) but misleading for prioritization because the config prerequisite effectively raises real-world complexity. |
| Affected versions | BIND 9.16.0–9.18.50, 9.20.0–9.20.29, 9.21.0–9.21.26, SPE 9.11.3-S1–9.18.50-S1, 9.20.9-S1–9.20.29-S1. Bug introduced in commits 3f81d79ffb / 34dd6521b1, first in tags 9.17.12 and 9.16.14. |
| Fixed versions | 9.20.30, 9.21.27, 9.20.30-S1. Debian Trixie: 1:9.20.30-1~deb13u1 (DSA-6576-1). RHEL: RHSA-2026:24339. |
| Exposure data | Censys/Shodan report ~706K internet-facing BIND recursive resolvers globally (per 2026 scans for related CVEs). However, the subset running RPZ with mixed recursive-only settings is estimated at <5% of that population. Most enterprise resolvers are internal-only behind ACLs. |
| Researcher | Vitaly Simonovich (also credited for CVE-2026-96888). Reported via ISC coordinated disclosure. |
| Disclosure timeline | Reserved 2026-09-24. Published 2026-10-21. Patches released same day. |
Sources.
- ISC Advisory: CVE-2026-97313
- ISC GitLab Issue #6408
- SecurityOnline: BIND 9 Vulnerabilities October 2026
- Debian Security Advisory DSA-6576-1
- Red Hat Errata RHSA-2026:24339
- BIND 9.21.28-dev Release Notes
- Canadian Centre for Cyber Security Advisory AV26-1072
- LinuxCompatible: ISC Releases BIND 9.20.30 and 9.21.27
Why this verdict
- Configuration gate is the primary friction. The vulnerability requires ≥2 RPZ zones with *mixed*
recursive-onlysettings. RPZ is a minority BIND feature (estimated 30–40% of enterprise BIND deployments). Mixedrecursive-onlyis a deliberate non-default choice within that subset—realistically <5–10% of RPZ users. This compounds to roughly 2–4% of all BIND installations being vulnerable. CVSS cannot encode this; we adjust downward by ~1.5 points. - DoS-only impact caps the ceiling. No code execution, no data exfiltration, no privilege escalation. The assertion failure kills
named, which systemd restarts in seconds. A sustained outage requires persistent attacker access and knowledge of the triggering QNAME. This is disruptive but recoverable. - Role multiplier: DNS recursive resolvers are critical infrastructure, but the outcome does not meet the HIGH floor. Internal DNS resolvers with RPZ are high-value components—an outage disrupts name resolution fleet-wide. However, the blast radius is *service disruption*, not domain takeover, fleet compromise, mass data egress, supply-chain pivot, or OT-safety impact. The HIGH floor requires one of those outcomes. DNS DoS is serious but falls short of the threshold. Additionally, enterprises with redundant DNS (secondary resolvers, stub forwarders, DoH fallback) limit the blast radius further.
- No exploitation pressure. Zero in-the-wild activity, no public PoC, no KEV listing, EPSS not yet scored but expected low. There is no urgency signal from the threat landscape.
- Timing window adds real friction. The second RD=0 query must arrive while the async RPZ fetch is still in flight. Network jitter, caching, and resolver load can close this window, making exploitation less reliable than the CVSS AC:L suggests.
Why not higher?
The configuration prerequisite disqualifies this from HIGH. The attack only works against the small subset of BIND servers running ≥2 RPZ zones with mixed recursive-only values—a non-default, unusual setup. The impact is DoS only, with no path to code execution or data compromise. The HIGH floor for critical-infrastructure roles requires outcomes like domain takeover or fleet compromise, which a DNS crash does not produce. No exploitation activity or public PoC exists to create urgency.
Why not lower?
Despite the narrow configuration gate, this is still a remotely-triggerable, unauthenticated crash in a network-critical service. Where the preconditions *are* met, the crash is deterministic and repeatable—an attacker can sustain a DNS outage. BIND is the most widely deployed DNS software, and RPZ-enabled resolvers are typically security-critical chokepoints. A LOW rating would understate the impact for the minority of operators who *are* vulnerable. MEDIUM correctly signals that affected operators should patch within 365 days while the unaffected majority can deprioritize.
Crowdsourced verification payload.
Run this script on each BIND DNS server host as root or a user with read access to named.conf. Invoke with: sudo bash check_cve_2026_97313.sh or optionally sudo bash check_cve_2026_97313.sh /path/to/named.conf. Exit code 0 = VULNERABLE, 1 = PATCHED, 2 = UNKNOWN.
#!/usr/bin/env bash\n# noisgate: CVE-2026-97313 verification\n# Checks BIND version + RPZ mixed recursive-only config\nset -uo pipefail\n\nCONF=\"${1:-/etc/named.conf}\"\nALT_CONF=\"/etc/bind/named.conf\"\n\nif [[ ! -f \"$CONF\" ]]; then\n if [[ -f \"$ALT_CONF\" ]]; then\n CONF=\"$ALT_CONF\"\n else\n echo \"UNKNOWN - Cannot locate named.conf at $CONF or $ALT_CONF\"\n exit 2\n fi\nfi\n\nNAMED_BIN=$(command -v named 2>/dev/null || true)\nif [[ -z \"$NAMED_BIN\" ]]; then\n echo \"UNKNOWN - named binary not found in PATH\"\n exit 2\nfi\n\nVERSION=$(\"$NAMED_BIN\" -v 2>/dev/null | grep -oP '\\d+\\.\\d+\\.\\d+' | head -1)\nif [[ -z \"$VERSION\" ]]; then\n echo \"UNKNOWN - Could not determine BIND version\"\n exit 2\nfi\necho \"Detected BIND version: $VERSION\"\n\nIFS='.' read -r MAJOR MINOR PATCH <<< \"$VERSION\"\n\nPATCHED=0\nif [[ \"$MAJOR\" -eq 9 ]]; then\n if [[ \"$MINOR\" -eq 20 && \"$PATCH\" -ge 30 ]]; then PATCHED=1; fi\n if [[ \"$MINOR\" -eq 21 && \"$PATCH\" -ge 27 ]]; then PATCHED=1; fi\n if [[ \"$MINOR\" -gt 21 ]]; then PATCHED=1; fi\nfi\n\nif [[ \"$PATCHED\" -eq 1 ]]; then\n echo \"PATCHED - BIND $VERSION is not affected by CVE-2026-97313\"\n exit 1\nfi\n\nVULN_VER=0\nif [[ \"$MAJOR\" -eq 9 ]]; then\n if [[ \"$MINOR\" -ge 16 && \"$MINOR\" -le 18 ]]; then VULN_VER=1; fi\n if [[ \"$MINOR\" -eq 20 && \"$PATCH\" -le 29 ]]; then VULN_VER=1; fi\n if [[ \"$MINOR\" -eq 21 && \"$PATCH\" -le 26 ]]; then VULN_VER=1; fi\nfi\n\nif [[ \"$VULN_VER\" -eq 0 ]]; then\n echo \"PATCHED - BIND $VERSION is outside the vulnerable range\"\n exit 1\nfi\n\necho \"Checking RPZ configuration in $CONF...\"\nRPZ_BLOCK=$(grep -A 200 'response-policy' \"$CONF\" 2>/dev/null | head -200 || true)\n\nif [[ -z \"$RPZ_BLOCK\" ]]; then\n echo \"PATCHED - No response-policy (RPZ) found; CVE-2026-97313 not exploitable\"\n exit 1\nfi\n\nZONE_COUNT=$(echo \"$RPZ_BLOCK\" | grep -c 'zone' 2>/dev/null || echo \"0\")\nif [[ \"$ZONE_COUNT\" -lt 2 ]]; then\n echo \"PATCHED - Fewer than 2 RPZ zones; CVE-2026-97313 requires at least 2\"\n exit 1\nfi\n\nHAS_REC_NO=$(echo \"$RPZ_BLOCK\" | grep -ci 'recursive-only no' 2>/dev/null || echo \"0\")\nif [[ \"$HAS_REC_NO\" -gt 0 && \"$ZONE_COUNT\" -ge 2 ]]; then\n echo \"VULNERABLE - BIND $VERSION with mixed RPZ recursive-only settings\"\n echo \" RPZ zones: $ZONE_COUNT, zones with recursive-only no: $HAS_REC_NO\"\n exit 0\nelse\n echo \"PATCHED - RPZ zones found but no mixed recursive-only settings\"\n exit 1\nfi