Interception
DNS spoofing
A forged DNS answer that sends a name to the attacker's address, believed by a client or cached by a resolver.
A plain DNS answer is UDP, unsigned, and accepted when its transaction ID, port and question match the query. An attacker on the path answers first; one off the path guesses the ID and port and floods forged replies, and a resolver that caches one hands the lie to everyone who asks until its TTL runs out. Random source ports make guessing hard, DNSSEC makes a forged answer fail validation, and encrypted DNS keeps the path from reading or answering for the real resolver.
How DNS spoofing works, step by step
The laptop (10.0.1.10) asks its resolver (10.0.1.11) for bank.lab, and the honest answer is
10.0.2.10. The attacker wants the answer to say 10.0.2.11, its own server. A plain DNS reply carries no proof,
so the attacker only has to get a false one accepted somewhere along the way:
- The laptop sends its question over UDP to port 53, with a random 16-bit query id and a random source port.
- The resolver asks the authoritative servers the same way, from its own random port and id.
- A reply is accepted if it comes from the address that was asked, to the right port, with the same id and the same question (RFC 5452). The first matching reply wins; a later honest one is thrown away.
- An off-path attacker guesses. It cannot see the query, so it floods forged replies with many ids. In 2008 Dan
Kaminsky showed how to make that pay: ask the resolver about random names (
x1.bank.lab,x2.bank.lab), so each one is a fresh race, and put the lie in the authority section ("bank.labis served by my server"). One win poisons the cache for the whole domain. Random source ports added about 16 more bits to guess, and resolvers now accept records only for the zone they asked about (the bailiwick rule). - An on-path attacker does not guess. On a hostile Wi-Fi, or after ARP spoofing, it reads the id and port from the query and answers first.
- Or the resolver itself lies: a resolver handed out by a hostile network, or one whose cache or configuration was changed. The poisoned answer is then served to every client until its TTL runs out.
The laptop then connects to whatever address it got. The name on screen never changes. A line in /etc/hosts does the same job locally, before DNS is even asked.
What it looks like on a Linux machine
In the DNS spoofing lab the resolver runs dnsmasq. Honest first:
$ dig bank.lab
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2518
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; ANSWER SECTION:
bank.lab. 0 IN A 10.0.2.10
;; SERVER: 10.0.1.11#53(10.0.1.11) (UDP)
A capture shows why the answer is easy to fake: question and answer cross in the clear, matched only by the id:
IP 10.0.1.10.56230 > 10.0.1.11.53: 32334+ [1au] A? bank.lab. (49)
IP 10.0.1.11.53 > 10.0.1.10.56230: 32334* 1/0/1 A 10.0.2.10 (53)
Then the resolver is made to lie. Same command, other machine, and a copied page that looks right:
$ dig +short bank.lab
10.0.2.11
$ curl -s http://bank.lab
hello from bank
<!-- copied by attackerServer -->
HTTPS notices, because the attacker cannot present the bank's certificate:
$ curl -sS --cacert /root/bank.pem https://bank.lab
curl: (60) SSL certificate problem: unable to get local issuer certificate
$ curl -sS -k https://bank.lab -o /dev/null -w '%{http_code} from %{remote_ip}\n'
200 from 10.0.2.11
-k is "proceed anyway", and it lands on the attacker. Pinning the real address gets past the lie for one request:
curl --resolve bank.lab:443:10.0.2.10 prints 200 from 10.0.2.10, with the certificate still checked.
What goes wrong, and how you see it
Plain HTTP shows nothing. The copied page loads, the status is 200. Only %{remote_ip} in curl, or
the page's source, tells you which machine answered. ping bank.lab also "works": it pings the attacker.
dig and curl disagree. dig asks DNS directly. Programs go through the C library, which follows
hosts: files dns in /etc/nsswitch.conf and reads /etc/hosts first. A planted line 10.0.2.11 bank.lab
sends curl to the attacker while dig shows the honest 10.0.2.10. getent hosts bank.lab resolves the way
programs do and exposes it.
The lie outlives the fix. Correct the resolver and clients may still hold the poisoned answer until its TTL
expires. Flush the resolver's cache (unbound-control flush bank.lab, or restart dnsmasq) and the client's if it
caches.
People click through. The browser's certificate warning is the whole defence once the name is spoofed. Every
"proceed anyway" is the -k above.
Questions people ask
Is DNS spoofing the same as DNS cache poisoning? Cache poisoning is one kind: the forged answer is stored in a resolver's cache and served to everyone behind it. DNS spoofing covers any forged answer, including one sent straight to a single client or written into its hosts file.
Does DNSSEC stop DNS spoofing? It stops forged answers between a validating resolver and the authoritative servers: an unsigned or altered answer for a signed zone is refused with SERVFAIL, as the DNSSEC lab shows. It does not protect the hop from the laptop to its resolver; DNS over TLS or HTTPS does that. See DNSSEC.
Does HTTPS protect me? Yes, as long as nobody clicks through the warning. Spoofing moves you to the wrong server; TLS proves which server you reached. Spoofing a source address at the IP layer is a different attack: IP spoofing.