⚠️ Educational use only. Everything demonstrated in this post and its companion lab runs on a k3d cluster on hardware you own. Running these techniques against networks, domains, or users you do not have explicit written authorization for is illegal in virtually every jurisdiction and unethical regardless of intent. The point of this post is to make defenders better — not to make attackers. If you don’t have a lab, build one before reading further.

TL;DR

The trust model DNS was born with assumed a friendly internet. UDP, no auth, no encryption, “first answer wins.” Decades of attacks have shown what happens when those assumptions meet reality. This post walks through the real incidents you should know — Kaminsky, DigiNotar, MyEtherWallet, Sea Turtle — explains how each class of attack works, and lays out the layered defenses that actually hold up: DNSSEC, DoT/DoH, CAA, registrar lock, subdomain takeover scanning, and zone monitoring.

Introduction

Years ago I got pulled into a client’s incident: their developers were getting weird TLS warnings on internal services, but only on the office Wi-Fi. The warnings came and went. Reboots fixed it for some people, didn’t for others.

It turned out a contractor’s laptop had been running a rogue DHCP server for weeks. Not maliciously — they’d installed something for a home lab and forgotten to disable it on their work machine. Half the office’s laptops were getting DHCP leases pointing at the contractor’s resolver. The contractor’s resolver was forwarding to a public DNS provider with NXDOMAIN-ad-injection turned on. Bypassing the corporate split-horizon DNS that mapped internal hostnames to internal IPs.

No malice, no skill, no zero-day. Just DNS doing what DNS does: trusting whoever it’s pointed at. Imagine the same scenario with malice involved.

The trust model DNS was born with

The original DNS protocol, as written into RFCs 1034 and 1035 in 1987, assumed:

  • The network is mostly cooperative. Nobody on your path is going to forge responses.
  • UDP is fine. Lower latency, no connection setup, fits in one packet.
  • Authentication is unnecessary. Whoever answers first wins.
  • Confidentiality doesn’t matter. Hostnames aren’t sensitive.

Every one of these assumptions has aged badly. The next sections walk through what happens when each one breaks.

Cache poisoning and Kaminsky (2008)

The classic attack: convince a recursive resolver to cache a forged answer. If you can do that, every client of that resolver gets your answer for the duration of the TTL. You don’t need to MITM the user; you poison the resolver.

The mechanic is straightforward. The attacker triggers the resolver to look up a name. While the resolver is waiting for the real authoritative answer, the attacker races a forged response back, with a guessed transaction ID. If the guess matches, the resolver caches the forgery as if it were authoritative.

This was treated as a low-impact theoretical issue for years because the transaction ID is 16 bits — guessing seems hard. Then in 2008, Dan Kaminsky showed how to make it easy. He demonstrated a birthday attack on the transaction ID combined with predictable source-port reuse: by triggering many lookups for random123.example.com, random124.example.com, and so on, the attacker gets many simultaneous chances at a collision. Within seconds, you can poison a major resolver.

The fix that shipped within months was source-port randomization — instead of a fixed source port, use a random one per query, adding ~16 bits of entropy. The attack space jumped from ~65k attempts to billions. Combined with longer-term work (DNSSEC, query-name minimization), this killed the practical version of the attack — but the fundamental issue, that DNS over UDP is unauthenticated, did not go away.

On-path attacks: malicious resolvers and ISP injection

If the attacker is on the network path between client and resolver, they don’t need to guess anything. They just lie. Open Wi-Fi networks, malicious DHCP servers (like my contractor’s lab), compromised home routers, or simply being your ISP — all of these positions let an attacker substitute responses.

NXDOMAIN hijacking is the most banal version. ISPs have been caught for years rewriting NXDOMAIN responses (the “no such domain” answer) into A records pointing at search-and-ad portals. It breaks DNSSEC validation, breaks software that depends on NXDOMAIN semantics, and quietly funnels every typo your users make through the ISP’s ad inventory.

The defense here is end-to-end: encrypt and authenticate the path between client and resolver. Which brings us to DoT and DoH (in defenses, below).

Registrar hijacks: Sea Turtle and DNSpionage (2018-2019)

Protocol defenses don’t help when the attacker is the legitimate operator of your DNS. If someone takes over your registrar account, they can rewrite your NS records. They control your zone. DNSSEC is irrelevant — they can re-sign with their own keys, or remove the DS record at the parent.

The Sea Turtle / DNSpionage campaigns, attributed by Cisco Talos and Mandiant to nation-state actors, did exactly this. They targeted ccTLD operators, registrars, and large enterprises in the Middle East and elsewhere. They didn’t break DNS — they broke the administrative panel in front of DNS, then used legitimate authority to redirect traffic. Affected organizations briefly served traffic through attacker-controlled infrastructure with valid-looking TLS certs (issued because the attackers controlled DNS validation).

The defense isn’t a protocol. It’s account hygiene: registrar lock, mandatory MFA on the registrar, separation of registrar credentials from your AWS/GCP root credentials, and monitoring for NS/DS record changes outside of approved change windows.

BGP + DNS: MyEtherWallet (April 2018)

A 2018 incident showed how DNS can be combined with other protocol weaknesses. Attackers BGP-hijacked a prefix containing Amazon Route 53 servers for about two hours. During that window, lookups for myetherwallet.com from affected resolvers got attacker-controlled answers. The attackers served a phishing site with a self-signed cert. Users who clicked through the cert warning lost approximately $152,000 worth of Ethereum.

The takeaway is sobering. Both BGP and DNS were operating exactly as designed. Each, individually, was operating “correctly.” The combined attack required no novel exploit. RPKI (Resource Public Key Infrastructure) for BGP and DNSSEC for DNS each address parts of this, but adoption is patchy and the gap between “can be defended” and “is defended” is substantial.

Subdomain takeover: the dangling CNAME

A modern, high-prevalence class of attack that needs no exotic skill. Pattern:

  1. Someone at your company creates marketing-q3.example.com as a CNAME to q3-promo.s3.amazonaws.com for a campaign
  2. Campaign ends. The S3 bucket is deleted. The CNAME record is forgotten.
  3. An attacker registers q3-promo as their own S3 bucket name (S3 names are globally unique but freed on deletion).
  4. They now control content served from marketing-q3.example.com — your domain, their content. Cookies, OAuth callbacks, TLS certs, all in scope.

Same pattern works against Heroku apps, GitHub Pages, Azure blobs, Fastly services, and any cloud platform with shared-namespace hostnames. The defense is automated scanning — tools like subjack, subzy, or custom CI checks that flag dangling CNAMEs before an attacker does.

DigiNotar and the case for CAA (2011)

If an attacker controls a Certificate Authority, DNS doesn’t even need to lie. They can simply mint valid certs for your domain and use them in their attack.

DigiNotar, a Dutch CA, was compromised in 2011. Attackers issued ~500 fraudulent certs, including one for *.google.com, used in MITM attacks on Iranian users. The browsers’ response was decisive: every major browser revoked trust in DigiNotar, and the company collapsed within a month.

The systemic response was CAA records (RFC 6844, 2013) — a DNS record type that tells CAs “only this CA is allowed to issue certs for my domain.” Every modern public CA must check CAA before issuance. So if an attacker compromises a different CA, they can’t issue for your domain because your CAA record says no.

$ dig +short example.com CAA
0 issue "letsencrypt.org"
0 issue "digicert.com"
0 issuewild ";"
0 iodef "mailto:[email protected]"

If you don’t have CAA records, set them today. It’s one of the cheapest, highest-value DNS security records in existence.

The lab: see it happen

The companion lab spins up four components in a k3d cluster:

  • legit-zone — a BIND-9 authoritative server with a DNSSEC-signed lab.dns zone
  • attacker — a dnsmasq resolver with a forged record for bank.lab.dns
  • verifying-resolver — Unbound configured with DNSSEC validation enabled
  • victim — a netshoot debug pod whose resolv.conf we manipulate

The demo sequence:

# Step 1 — point victim at attacker's resolver (simulates DHCP poisoning)
kubectl exec victim -- dig bank.lab.dns
# Returns FORGED IP — attack succeeds, no defense in place

# Step 2 — switch victim to the DNSSEC-validating resolver
kubectl exec victim -- dig +dnssec bank.lab.dns
# Returns real IP, with `ad` (authenticated data) flag set

# Step 3 — attempt to inject a forged response at the verifying resolver
kubectl exec attacker -- python3 /scripts/forge_response.py
kubectl exec victim -- dig +dnssec bank.lab.dns
# Returns SERVFAIL — DNSSEC validation rejects the unsigned forgery

The Scapy script in step 3 prints the forged packet’s structure so you can see what a forgery actually looks like on the wire — transaction ID, question section, answer section, missing RRSIG. It is intentionally not weaponized: no race against real responses, no targeting of public infrastructure. Educational only.

Full setup, manifests, and walkthrough live in the companion repo.

The defenses, layered

No single defense covers all the attacks above. You need layers.

DNSSEC authenticates the chain from root to your zone. It proves origin and integrity, not confidentiality. It defeats classic cache poisoning and on-path response forgery. It does not defeat registrar hijacks (the attacker re-signs with their own keys) or BGP hijacks of resolvers. Adoption is patchy because it’s operationally annoying — KSK rollovers, DS record updates at the parent — but every major TLD supports it and Route 53/Cloud DNS/Azure DNS all offer one-click signing now. You should turn it on.

DoT (DNS over TLS, port 853) and DoH (DNS over HTTPS, port 443) encrypt the stub-to-resolver hop. They prevent on-path observation and modification of your queries. They do not sign the data — DoH from a malicious resolver still gives you malicious answers, just confidentially. DoT/DoH and DNSSEC are complementary: confidentiality plus integrity.

CAA records prevent rogue CA issuance, as discussed.

Registrar lock + mandatory 2FA + DNSSEC at the registrar is the human-layer defense against Sea Turtle-class attacks. Treat your registrar account as a Tier-0 system. Most teams don’t.

MTA-STS, TLSRPT, and DANE are the email-flavored defenses. MTA-STS publishes via HTTPS that your domain requires TLS for incoming mail. TLSRPT publishes a reporting endpoint. DANE pins TLS certs to DNS via TLSA records (requires DNSSEC). If you run mail, all three matter.

Subdomain takeover scanning in CI catches dangling CNAMEs before attackers do. Cheap, high value, run weekly.

Zone monitoring alerts you when NS, DS, or SOA records change outside approved windows. The number of organizations whose registrar’s email alerts have been silently misconfigured for years is staggering. Monitor your zone from the outside, with something you control.

What I tell clients

Five things, in priority order:

  1. DNSSEC-sign your zones. Yes, it’s annoying. Do it anyway.
  2. Lock your registrar account harder than your AWS root account. It’s a higher-value target, and most teams don’t realize it.
  3. Publish CAA records. Today. It’s a five-minute change with outsized impact.
  4. Scan for dangling CNAMEs in CI. Subdomain takeover is the easiest attack on this list to prevent.
  5. Monitor your own zone from the outside. Don’t trust the registrar’s alerting.

DNS isn’t a “set and forget” system. It’s a continuously verified one. Treat it that way.

Closing the series

We started this series with a hosts file at SRI-NIC and ended with DNSSEC, CAA, and registrar hardening. The journey isn’t accidental: every record type, every protocol decision, every defensive layer in DNS is a direct response to something that broke at scale.

The internet’s quiet backbone has been quietly carrying everything we build for forty years. It deserves more than to be the thing your team blames when an outage happens. Read the records. Sign the zones. Lock the registrar. Run the labs.

And if you want a ready-made lab to start practicing in, the repo is right there.


Lab — Try It Yourself

Repo: github.com/hagzag/dns-evolution-in-practice Lab: practice/part4/

task part4:run

The lab runs entirely locally on k3d. Nothing in it touches the public internet beyond pulling container images. Everything is reset by task part4:cleanup.


Further Reading