---
title: How to detect DNS hijacking before your customers do
description: A hijacked DNS record doesn't take your site down. It sends your users somewhere else, often behind a valid padlock. Four real incidents, the records worth watching, how to lock down your registrar, and how to get an alert the moment an answer stops matching what you expect.
canonical: https://watchfor.io/blog/detect-dns-hijacking
---

# How to detect DNS hijacking before your customers do

2026-10-03 · WatchFor Team · Security · security, dns, monitoring

A hijacked DNS record doesn't take your site down. It sends your users somewhere else, often behind a valid padlock. Four real incidents, the records worth watching, how to lock down your registrar, and how to get an alert the moment an answer stops matching what you expect.

Most outages announce themselves. A DNS hijack usually doesn't. The site
still loads, the padlock is still there, and your own dashboards stay green,
because your servers are fine. Your users are simply talking to someone
else's.

In the cases below, the people affected found out from intercepted logins,
from a security vendor's report, or from spam sent in their name. None of
them found out from an alert that said "your DNS answer changed". That alert
is cheap to build, and this post shows how.

## What DNS hijacking looks like in practice

"DNS hijacking" covers several different attacks. They need different
defences, so it's worth separating them. Each of these happened, and each
one is documented by the victim, the researchers or a government agency.

### 1. Someone logs in to your registrar: Fox-IT, 2017

On 19 September 2017 an attacker logged in to the third-party domain
registrar of Fox-IT, a Dutch security company, with a valid username and
password. Fox-IT's
[own write-up](https://blog.fox-it.com/2017/12/14/lessons-learned-from-a-man-in-the-middle-attack/)
is unusually candid. The password hadn't been changed since 2013, because DNS
settings "change very rarely", and the registrar didn't support two-factor
authentication at all.

The attacker pointed the domain's name servers elsewhere, redirected Fox-IT's
email for at most 10 minutes (long enough to pass a certificate authority's
email validation and get a real certificate for the client portal), then
pointed `clientportal.fox-it.com` at a server abroad. The interception lasted
10 hours and 24 minutes. Nine users' credentials were captured, though
two-factor authentication on the portal stopped them from being reused.

Note the order: the certificate came first. By the time traffic was
redirected, the fake server had a valid padlock.

### 2. Someone compromises the registrar itself: Sea Turtle, 2017 to 2019

Cisco Talos
[published Sea Turtle](https://blog.talosintelligence.com/seaturtle/) in
April 2019: a state-sponsored campaign that, from as early as January 2017,
compromised at least 40 organizations in 13 countries, mostly in the Middle
East and North Africa. The attackers didn't phish each target's DNS
password. They broke into the registrars, telecoms and, in one case, a
registry that served those targets, and changed records from there.

Like Fox-IT's attacker, they obtained certificates for the hijacked names
from a different CA than the victim normally used. Talos calls this
"certificate impersonation": the padlock still showed, so users had no
reason to stop.

The US government's reaction is the clearest checklist you'll find. CISA's
[Emergency Directive 19-01](https://www.cisa.gov/news-events/directives/ed-19-01-mitigate-dns-infrastructure-tampering)
of 22 January 2019 described attackers changing A, MX and NS records after
stealing DNS account credentials, and gave federal agencies 10 business days
to do four things: audit their public DNS records, change DNS account
passwords, add multi-factor authentication to those accounts, and monitor
Certificate Transparency logs.

### 3. Your zone is untouched, but the internet routes around it: MyEtherWallet, 2018

On 24 April 2018 eNet (AS10297) announced more-specific routes for address
space belonging to Amazon's Route 53 DNS service for roughly two hours.
[Cloudflare's analysis](https://blog.cloudflare.com/bgp-leaks-and-crypto-currencies/)
explains the effect: resolvers whose networks accepted the route sent their
queries to impostor servers that answered only for `myetherwallet.com`, with
addresses at a Russian hosting provider. Cloudflare's own 1.1.1.1 resolver
was affected in eleven locations, including Chicago and Sydney, and worked
normally elsewhere.

Users who clicked through a browser warning about a self-signed certificate
gave away their wallet logins, and the attacker used them to steal Ether.

The detail that matters for detection: **nothing in MyEtherWallet's DNS
changed.** The zone in Route 53 was correct the whole time. Only resolvers in
some regions got wrong answers. A check from your office, or a look at your
DNS provider's dashboard, would have shown that everything was fine.

### 4. A record you forgot about: dangling CNAMEs and SubdoMailing

A subdomain points with a CNAME at a cloud resource or a third-party domain.
Years later the resource is deleted or the domain lapses, but the CNAME
stays. Whoever claims that name at the other end now controls your
subdomain. Microsoft's guide to
[dangling DNS and subdomain takeover](https://learn.microsoft.com/en-us/azure/security/fundamentals/subdomain-takeover)
walks through the pattern on Azure, and it's the same on every cloud.

In February 2024 Guardio Labs
[documented "SubdoMailing"](https://guard.io/labs/subdomailing-thousands-of-hijacked-major-brand-subdomains-found-bombarding-users-with-millions):
more than 8,000 domains, including MSN, VMware, McAfee, The Economist, CBS
and eBay, used to send millions of spam and phishing emails a day. One
example: `marthastewart.msn.com` was a CNAME to `msnmarthastewartsweeps.com`,
a domain that was abandoned for about two decades and then registered again
in September 2022. The new owner published an SPF record on it that
authorized more than 17,000 IP addresses, and mail from them passed SPF as an
`msn.com` subdomain.

Nobody touched Microsoft's DNS. The record that made the attack possible had
been sitting there for years, pointing at a domain nobody owned.

## Which DNS records to watch

Each record type fails in its own way when it's hijacked. Watch the ones you
have, and know what "correct" looks like for each. If you need a refresher on
the types themselves, see
[DNS record types explained](/blog/dns-record-types-explained).

| Record | What a hijack looks like | How to watch it |
| --- | --- | --- |
| **NS** | Your whole domain delegated to someone else's name servers (Fox-IT) | Your exact name servers, checked at the parent zone too |
| **A / AAAA** | Your hostname resolves to an address you don't own | The set of addresses you or your CDN actually use |
| **MX** | Mail goes to the attacker, who can now pass email-based certificate validation | Your mail hosts |
| **TXT (SPF)** | New `include:` or `ip4:` entries authorize someone else to send as you | The full SPF string, or the includes you expect |
| **TXT (verification)** | A new `google-site-verification` or similar token lets someone claim your domain in a service | No unexpected tokens |
| **CAA** | Removed or widened, so any CA will issue for you | The CAs you use |
| **CNAME** | Points at a resource nobody owns any more | Inventory, plus content checks on the subdomain |
| **SOA** | The serial jumps when nobody on your team changed the zone | The serial, as a cheap "something changed" signal |

Two of these deserve a closer look.

**Check NS at the parent, not only at your own servers.** If an attacker
changes the delegation at the registry, your own name servers keep answering
correctly. They just stop being asked. Query the TLD's servers directly:

```bash
tld=$(dig +short NS org. | head -1)
dig +norec +noall +authority NS wikipedia.org @"$tld"
```

```text
wikipedia.org.		3600	IN	NS	ns1.wikimedia.org.
wikipedia.org.		3600	IN	NS	ns2.wikimedia.org.
wikipedia.org.		3600	IN	NS	ns0.wikimedia.org.
```

**"Expected" is often a set, not a value.** Wikimedia sends users to the
nearest data centre, so `wikipedia.org` has a different A record depending on
who asks. You can see it by asking their name server on behalf of different
client networks:

```bash
for subnet in 8.8.8.0/24 81.2.69.0/24 133.0.0.0/16 200.160.0.0/20; do
  echo "$subnet $(dig +short A wikipedia.org @ns0.wikimedia.org +subnet=$subnet)"
done
```

```text
8.8.8.0/24 208.80.154.224
81.2.69.0/24 185.15.59.224
133.0.0.0/16 103.102.166.224
200.160.0.0/20 195.200.68.224
```

A US network, a UK one, a Japanese one and a Brazilian one get four different
answers. A naive "the answer must equal X" check would fire from most of the
world. List every legitimate answer for the places you check from, then
alert on anything outside that list.

## Lock down the registrar and the zone first

Detection buys you minutes. Prevention removes most of the attacks above.

1. **Treat the registrar and DNS accounts as production credentials.**
   Unique passwords, phishing-resistant multi-factor authentication
   (hardware keys where the provider supports them), and as few people with
   access as possible. Fox-IT's closing advice is worth repeating: ask
   whether your registrar supports 2FA. "The answer may surprise you."
2. **Turn on registrar lock, and ask about registry lock.** ICANN's
   [EPP status codes](https://www.icann.org/resources/pages/epp-status-codes-2014-06-16-en)
   distinguish the two. `clientTransferProhibited` and
   `clientUpdateProhibited` are set by your registrar and stop transfers and
   updates until the lock is removed, which anyone logged into your account
   can do. The `server*Prohibited` statuses are set at the registry and
   usually need an out-of-band request to lift; that's what registrars sell
   as "registry lock", and what Talos recommended after Sea Turtle. Check
   your current status with RDAP:

   ```bash
   curl -s https://rdap.publicinterestregistry.org/rdap/domain/wikipedia.org | jq .status
   ```

   ```json
   ["client delete prohibited", "client transfer prohibited", "client update prohibited"]
   ```

3. **Sign the zone with DNSSEC.** DNSSEC lets validating resolvers reject
   forged answers, which is exactly the MyEtherWallet scenario: the impostor
   servers couldn't have produced valid signatures. It doesn't help when the
   attacker controls your registrar account, because they can change the name
   servers and remove the DS record too. [DNSSEC explained](/blog/dnssec-explained)
   covers the trade-offs. Since 15 March 2026 CAs have also been
   [required](https://github.com/cabforum/servercert/blob/main/docs/BR.md) to
   validate DNSSEC on the lookups they use for domain validation and CAA, so
   a signed zone makes it harder to get a certificate with forged answers.
4. **Publish CAA records** naming the CAs you use. Wikipedia's are a good
   example: `0 issue "letsencrypt.org"`, `0 issue "pki.goog"` and an `iodef`
   address for reports. CAs SHOULD already honour the `accounturi` parameter,
   which ties issuance to your ACME account, and MUST from 15 March 2027.
5. **Delete DNS records before you delete the resource.** When a cloud app,
   bucket or SaaS integration goes away, remove its CNAME first. Once a
   quarter, list every CNAME that points outside your own domains and check
   that the target still belongs to you.
6. **Watch Certificate Transparency.** In Fox-IT's and Sea Turtle's cases the
   attackers needed a certificate for your name, and every public certificate
   is logged. A certificate from a CA you don't use is a strong signal.

## Alert on unexpected answers

The detection idea is simple: write down what each record should be, ask
regularly, and alert on any difference. A short script gets you surprisingly
far:

```bash
#!/usr/bin/env bash
# Compare DNS answers against a baseline. Exits non-zero if anything differs.
fail=0
check() { # check NAME TYPE EXPECTED...
  local name=$1 type=$2; shift 2
  local got want
  got=$(dig +short "$type" "$name" @1.1.1.1 | sort | paste -sd' ')
  want=$(printf '%s\n' "$@" | sort | paste -sd' ')
  if [[ "$got" != "$want" ]]; then
    echo "CHANGED $type $name: got [$got] expected [$want]"
    fail=1
  fi
}
check wikipedia.org NS  ns0.wikimedia.org. ns1.wikimedia.org. ns2.wikimedia.org.
check wikipedia.org MX  "10 mx-in1001.wikimedia.org." "10 mx-in2001.wikimedia.org."
check wikipedia.org CAA '0 iodef "mailto:dns-admin@wikimedia.org"' '0 issue "letsencrypt.org"' '0 issue "pki.goog"'
exit $fail
```

Run it from cron and you'll notice a changed record. It has three blind
spots, and they're the ones that mattered in the incidents above:

- **One vantage point.** It asks one resolver from one network. The
  MyEtherWallet answers were wrong only in some regions.
- **Caching.** A resolver can keep serving a cached answer for the record's
  TTL after the zone changes, in either direction.
- **Silence.** If cron stops running, nothing tells you. That's a
  [heartbeat](/blog/heartbeat-cron-monitoring) problem of its own.

Two more design points, whatever you use:

- **Check from several networks and several resolvers,** including your own
  authoritative servers. A difference between your authoritative answer and
  what a public resolver in another country returns is the most useful
  signal there is.
- **Send these alerts to someone who can act on a security incident,** not
  only to the on-call channel for the website. A changed NS record at 3 a.m.
  is not a performance problem.

## Watching DNS answers with WatchFor

WatchFor's DNS monitor runs that lookup on a schedule from the locations you
choose, and records what came back each time. It supports A, AAAA, CNAME, MX,
NS, TXT, SOA, SRV, PTR and CAA. By default each location uses its own
resolver. You can switch to Cloudflare, Google, Quad9, OpenDNS, or any IP
address, including your own authoritative server, to catch a stale or
tampered zone before the rest of the world caches it.

Here is the "wikipedia.org DNS" monitor from our demo organization, checking the
A record every five minutes:

![The WatchFor DNS monitor for wikipedia.org: up, 100% uptime, lookup time 28.4 ms, and a DNS Records card showing an A query with NOERROR, one answer, Expected match, the four expected Wikimedia addresses and the latest record 185.15.59.224](/blog/detect-dns-hijacking-monitor.webp)

### The Expected DNS Records rule

The rule that turns this into hijack detection is **Expected DNS Records**.
You list the legitimate values, one per line, and pick how to compare them:

![The Expected DNS Records rule on the Alerting tab, with match mode set to "Any record matches exactly" and four expected IPv4 addresses (185.15.59.224, 185.15.58.224, 208.80.154.224, 208.80.153.224) at critical severity, below the No Records Returned and DNS Errors rules](/blog/detect-dns-hijacking-expected-records.webp)

- **Any record matches exactly** suits geo-routed A and AAAA records like
  Wikipedia's: one of the listed addresses must come back. The demo monitor
  lists four because it checks from Europe; checking from Asia or South
  America as well would mean adding the addresses those regions get.
- **All values must be present** suits NS and MX: every one of your name
  servers or mail hosts must be in the answer.
- **Any record contains substring** suits long TXT records such as SPF, for
  example `include:_cidrs.wikimedia.org`.
- **No record may contain (forbidden)** catches known-bad answers, such as
  a CNAME target you have decommissioned or a parking provider's name
  servers.
- **Any record matches regex** covers everything else.

Each probe result shows the answer next to what you expected, so when an
incident opens you can see the exact difference rather than "check failed":

![A DNS probe result: query A, response code NOERROR, the four expected addresses, match mode "Any record matches exactly", and the record that came back, A 185.15.59.224](/blog/detect-dns-hijacking-probe-result.webp)

In practice that means a handful of small monitors per domain: NS for the
apex with **All values must be present**, MX the same way, a TXT monitor
for SPF with **contains**, CAA, and A/AAAA for the hostnames that matter.
The other DNS rules (**DNS Errors**, **No Records Returned** and lookup time)
catch the outage-shaped failures alongside them.

Be clear about one limit. The rule checks that your expected values are in
the answer. It doesn't fire when an attacker *adds* a record next to yours,
for example a second A record or an extra TXT verification token. For TXT,
a **forbidden** list helps with patterns you can predict. For the rest, the
record history on each check shows what was there.

Every check runs from the locations you pick, each through its own resolver
unless you choose one, and failures are confirmed from several locations
before anyone is alerted. When one region sees a different answer, that
location's probe results show it. DNS monitors are available from the
Starter plan up. The [DNS monitor docs](/docs/monitors/network#dns) list
every setting and the expected value format for each record type.

Subdomain takeovers are the exception to everything in this section: the
CNAME doesn't change, only who owns its target. For those, inventory is the
real fix, and an HTTP check with a [keyword rule](/blog/keyword-monitoring)
on the subdomain will tell you when the page stops being yours.

### A one-off check from 20 locations

To see what the world resolves right now, without an account, use the free
[DNS propagation checker](/dns-propagation-checker). It sends the same query
from every WatchFor probe location at once and puts the answers on a map.
After a suspected change, it's the fastest way to see whether the wrong
answer is everywhere or only in some regions:

![The free DNS propagation checker querying the NS records of wikipedia.org: 20 of 20 locations responded, Cape Town, Jakarta, Mumbai and Singapore all returning ns0, ns1 and ns2.wikimedia.org, with green markers on a world map](/blog/detect-dns-hijacking-propagation-checker.webp)

For single lookups there are also the [DNS checker](/dns-checker),
[NS lookup](/ns-lookup) and [CAA lookup](/caa-record-lookup) tools.

## A checklist to finish today

1. Turn on multi-factor authentication for your registrar and DNS
   provider, and remove accounts that don't need access.
2. Check your EPP status with RDAP; enable registrar lock, and ask about
   registry lock for the domains that matter most.
3. Write down the expected NS, MX, SPF, CAA and A/AAAA values for each
   domain.
4. Monitor them from several locations, with an alert that goes to someone
   who can respond to a security incident.
5. Publish CAA records, consider DNSSEC, and watch Certificate Transparency
   logs.
6. List every CNAME that points outside your domains and delete the ones
   nobody owns.

If the attacks in this post have one lesson, it's that the attacker's
first move is rarely visible on your servers. It happens at the registrar, in
the routing table or in a record nobody has looked at in years. The only
place it shows up is in the answers other people get. For how those answers
get to people in the first place, see [how DNS works](/blog/how-dns-works),
and for the email side of impersonation, see
[domain spoofing](/blog/domain-spoofing).

---

Canonical page: https://watchfor.io/blog/detect-dns-hijacking · All articles: https://watchfor.io/blog/all.md · Site guide: https://watchfor.io/llms.txt
