BIND 8 vulnerabilities and the discipline of running DNS

BIND — the Berkeley Internet Name Daemon, currently in version 8.2 — is, by an enormous margin, the most widely deployed DNS server on the internet. The internet's name system runs, in practice, on BIND.

The last twelve months have been hard for BIND. Multiple serious advisories: cache poisoning vulnerabilities, buffer overflows in record handling, denial-of-service issues. The cumulative pressure has produced BIND 8.2.2, released last week, which addresses the worst of them.

I run a small DNS server for my own domain. I have been re-reading the configuration this week with fresh eyes. The exercise is worth writing down because the principles generalise.

What DNS gets wrong

DNS is unauthenticated. It always has been. When your machine asks 8.8.8.8 for the IP of example.com, the answer comes back over UDP, with no cryptographic guarantee that the answer was produced by the legitimate authoritative server for example.com.

This architectural property has been understood as a problem since at least 1993. Several mitigations have been proposed and a few partially deployed; DNSSEC is the most prominent and is still in early stages of standardisation.

In the absence of authentication, the entire defence rests on the resolver only accepting answers that look like they could be in response to a question it asked. The resolver remembers outstanding queries; when an answer arrives, it checks the answer against the list of outstanding queries; if there is a match, the answer is accepted into the cache.

The match is by transaction ID — a 16-bit number — plus source/destination IP and port. An attacker who can guess these can produce a forged response that the resolver accepts. The forged response can claim that bank.example.com resolves to whatever IP the attacker wants. The cache accepts it. Every subsequent lookup for bank.example.com from this resolver returns the wrong answer.

This is cache poisoning. It has been the central security problem of DNS since the protocol was first designed. Most BIND advisories of the last year are, ultimately, about reducing the chance that an attacker can land a successful poisoning attempt.

What BIND 8.2.2 actually fixes

The specific issues addressed in 8.2.2 include:

A buffer overflow in NXT record handling. This is a memory-corruption bug, similar in shape to the wu-ftpd vulnerabilities I wrote about. A malformed NXT record received from a remote name server could overflow a buffer in BIND's record-parsing code, potentially leading to remote code execution.

A denial-of-service in SIG record handling. A specifically-crafted SIG record could send the BIND daemon into an infinite loop. Consequence: DNS unavailable for that resolver.

Improved randomness in transaction IDs. Earlier BIND versions used a less-than-fully-random transaction ID, in some cases reusing IDs in a predictable sequence. This made cache poisoning easier than it should have been. 8.2.2 strengthens the random number generator.

Improved port randomisation. Similarly, the source port for outgoing queries was not always random in earlier versions. Predictable source ports reduce the search space an attacker has to brute-force when poisoning a cache.

The last two are not vulnerabilities in the strict sense. They are defence improvements — making the unauthenticated protocol harder to attack in practice, even though the protocol itself remains structurally weak.

What I changed on my own resolver

A short procedure that took me an evening, applied to a Slackware 4.0 box running BIND 8.2.2.

First, the obvious: upgrade. The patched 8.2.2 went on. I verified the version with dig @localhost version.bind chaos txt, which returns the running version of the BIND daemon. The output should match what I just installed.

Second, restrict who can use the resolver. My machine should only answer recursive queries from my own network. By default, BIND will recurse for anyone, which makes it a useful tool for various forms of abuse. The configuration directive is:

options {
    allow-recursion { 192.0.2.0/24; 127.0.0.1; };
    allow-query { any; };
};

This says: anyone can ask me about zones I am authoritative for; only my own network can use me as a recursive resolver. The distinction matters and is often missed.

Third, run BIND as a non-root user in a chroot. The configuration is:

named -u bind -t /var/named/chroot -c /etc/named.conf

With the appropriate file structure under /var/named/chroot. Any compromise of BIND now lands the attacker in a chrooted environment running as bind, which is dramatically less useful than landing them as root in the full filesystem.

Fourth, log everything. The default BIND logging is sparse. I have it now writing every query, every response, every refused query, to a separate logfile that my log scanner reads. The disk cost is not zero — DNS is high-volume — but it is bounded, and the visibility is worth the cost.

Fifth, version masking. By default, dig @host version.bind chaos txt returns the BIND version, which is useful information for an attacker (it tells them which exploits to try). The configuration directive is:

options {
    version "unspecified";
};

This is mild security through obscurity. It does not stop a determined attacker. It does cut down the number of opportunistic probes that find a useful target.

The general lesson

BIND is critical infrastructure that has been written, for two decades, in C, with all the memory-safety problems that implies, on top of an unauthenticated protocol that was designed in a more trusting era. The community is doing a good job of patching the implementation. The protocol itself is harder to fix.

For an operator, the practical posture is:

  1. Run the latest BIND version, with all the defence-in-depth options enabled.
  2. Restrict recursion to your own users. Refuse it to everyone else.
  3. Run BIND chrooted and unprivileged.
  4. Log everything.
  5. Watch for advisories.

None of this is glamorous. All of it is the basic hygiene that lets you sleep at night with a DNS server on a public IP.

The wider question — what to do about the protocol itself — is going to dominate DNS for the next ten years. DNSSEC is one answer; not a complete one, and adoption is going to be slow. There are arguments that an entirely new naming protocol is needed; I find these unconvincing on backwards-compatibility grounds. The most likely future is incremental hardening of BIND-style implementations, gradual deployment of DNSSEC for the parts of the namespace where the parties care enough, and continued operational discipline for everyone else.

It is a long road. We are about a third of the way down it.


Back to all writing