Why Random Ports and TXIDs Were Not Enough for BIND 9

BIND 9 is the subject of this series, but the question reaches beyond one software release: why did defenses that randomize both a DNS transaction ID and a UDP source port fail to provide the expected protection against cache poisoning? The answer begins with architecture. We need to see who asks DNS questions, who stores the answers, and which component stands between a forged packet and many later connections. With that map in place, the BIND 9 case becomes a precise lesson about security assumptions: randomness is only as independent as the mechanism that produces it.

The DNS roles that determine the blast radius

When an application needs to connect to service.example, it normally does not ask an authoritative name server directly. A client-side stub resolver forms the DNS query and sends it to a recursive resolver configured by the operating system, network, or application. The recursive resolver may already have the answer in its cache. If it does not, it either follows the DNS delegation hierarchy itself or sends the question to an upstream forwarder. An authoritative name server supplies records for zones it controls; it is the source of an answer, not normally the shared cache that serves an organization’s users.

That distinction is the first important boundary in the BIND 9 story. A recursive resolver is a shared decision point. It accepts information from outside, decides whether that information belongs to a pending query, stores the result, and then serves the result to many clients. If the stored answer is false, the immediate victim is not only the person whose query created the cache entry. Other users asking for the same name, or for names below a poisoned delegation, may receive the resolver’s false answer as if it were established DNS data.

The USENIX study describes BIND 9 as the most widely deployed open-source DNS resolver and cites historical measurements that put it at roughly 42% of observed resolvers in 2011 and found BIND 9 resolvers behind 47% of the SMTP servers in the Alexa Top-1K list in 2016. Those figures are historical estimates, not a current census, but they explain why a flaw in a common recursive implementation deserves attention beyond the machine on which it runs. The ISC advisory for CVE-2025-40780 is more specific about the affected role: resolvers are affected, while authoritative services are believed to be unaffected by this vulnerability.

The difference between a recursive and an authoritative service is not a claim that authoritative DNS is generally safe. It is a statement about where this particular failure occurs. A server that only answers from its own zone data does not need to predict the identifiers and source port of an outbound recursive query. A recursive BIND service does. A forwarder can still matter because it performs the resolution work on behalf of a downstream client, even when it sends its questions to another resolver rather than directly to an attacker-controlled authoritative server.

The cache is therefore more than a performance feature. It is a trust boundary with persistence. A normal response from an authoritative server is accepted because it matches the resolver’s outstanding request and satisfies the protocol’s checks. A forged response that passes those checks can be stored for the record’s time-to-live and reused by later clients. The NIST Secure DNS Deployment Guide describes poisoned recursive caches, forged responses, and query prediction as established threats to recursive and forwarding services. It also recommends treating Internet-facing authoritative and recursive roles differently, because allowing recursion creates an attack surface that an authoritative-only service does not have.

The path can be summarized as follows:

flowchart LR APP["Client application"] --> STUB["Stub resolver"] STUB --> RES["BIND 9 recursive resolver"] RES --> CACHE["Shared cache"] RES --> AUTH["Legitimate authoritative server"] RES --> FWD["Optional forwarder"] FWD --> UP["Upstream DNS service"] AUTH -->|"Legitimate response"| RES UP -->|"Forwarded response"| RES FORGED["Off-path forged response"] -.->|"Attempts to match pending query"| RES CACHE -->|"Accepted answer"| STUB STUB --> APP

The key point is the direction of trust. The client trusts the recursive resolver; the recursive resolver must temporarily trust a response from somewhere else; and the cache turns that one accepted response into a reusable answer. A vulnerability that helps an off-path attacker make the forged response look like the expected response attacks the link between the resolver and its upstream sources. It does not require the attacker to replace the authoritative server’s zone file.

Why TXIDs and source ports became paired defenses

DNS responses need a way to be associated with the query that caused them. One part of that association is the transaction ID, or TXID: a 16-bit value in the DNS header that the resolver places in an outbound query and expects to see echoed in the response. An off-path attacker—one who cannot simply read the resolver’s traffic—would need to guess the right value while also supplying a response that matches the relevant name and query type. Before 2008, many resolvers relied heavily on this identifier and on the assumption that a single guessing round was unlikely to succeed.

That assumption did not survive the cache-poisoning technique disclosed by Dan Kaminsky. The attacker did not have to overwrite the desired address record directly. By racing a forged delegation containing name-server and glue records, an attacker could influence where later lookups went. Repeated attempts became more practical because the attacker could provoke queries for names that were not yet cached and try again without waiting for the target record’s normal expiration. The USENIX paper presents this history as the reason DNS implementations added another source of uncertainty: randomizing the UDP source port used for an outbound query.

The source port mattered because a response had to reach the expected socket as well as carry the expected transaction ID. If the two values were genuinely unpredictable and independent, an off-path forgery would face a much larger combined search space. The post-Kaminsky defenses therefore made sense. They did not claim that one 16-bit number was a cryptographic authenticator. They tried to make the resolver’s next query hard to identify and the corresponding response hard to deliver to the right place.

This distinction matters because the defense was often described in terms of visible fields: one random value in the header and another in the network endpoint. But a field is not a source of entropy. It is an output of some process. If the process is predictable, a larger-looking combination can provide much less protection than its nominal bit count suggests. If both values come from the same stateful process, observing one kind of output may reveal information about the other. The two locks are then not independent locks; they are two views of one mechanism.

The BIND 9 study contrasts its result with earlier post-Kaminsky attacks that inferred ports through operating-system side channels or brute-forced the TXID for each port candidate. Those approaches could require large bursts of unusual traffic, depend on behavior that stateful firewalls might discard, or fail when network address translation rewrote ports. The authors report a different capability: recovering enough information about BIND’s pseudo-random state to predict both the source port and TXID, often with only a small number of candidates and ordinary-looking DNS queries. The later chapters will examine how that claim works and how strongly the experiments support it. For now, the important point is narrower: the security of the two fields cannot be evaluated without examining their generator and the other places where its output appears.

The BIND 9 question is shared predictability, not randomization itself

The ISC advisory identifies CVE-2025-40780 as a high-severity, remotely exploitable BIND 9 vulnerability caused by a weakness in the pseudo-random number generator. It says that an attacker may predict the source port and query ID BIND will use, and that a successful spoof can cause BIND to cache attacker responses. The advisory assigns a CVSS score of 8.6 and names no known workaround. Those facts establish operational urgency, but they do not by themselves explain the mechanism or prove that every DNS implementation with randomized fields has the same weakness.

That boundary will guide this series. The subject remains BIND 9 and its relationships with recursive operators, forwarders, authoritative servers, clients, and ISC. The central theme is layered assurance: a protocol defense should not be treated as independent merely because its outputs appear in different fields, and no single control should be asked to provide integrity, privacy, access control, detection, and availability at the same time. The research shows how a shared PRNG can turn a local implementation choice into a system-level exposure. The advisory shows why the root fix is a version and patch decision. NIST’s guidance shows why the remaining response must still include other layers.

So the title’s claim is deliberately limited. Random source ports and transaction IDs were sensible defenses against the attacks known at the time. They were not enough for the tested BIND 9 configurations because predictability in a shared generator weakened both at once. The real question is therefore not whether a packet contains random-looking values. It is what generates those values, what else that state controls, how an observer can learn from the outputs, and which independent safeguards remain if the assumption of unpredictability fails.

References