Dev.to Security 🔐 Cybersecurity 👁 0 📖 9 min read

Subdomain Takeover & Dangling DNS Reconnaissance: Auditing CNAME, NS, TXT & TLS Certificates

A marketing team launches a campaign site on a hosted platform, points promo.example.com at it with a CNAME, and the campaign ends. Someone deletes the app on the platform. Nobody deletes the DNS record. Months later, an

Subdomain Takeover & Dangling DNS Reconnaissance: Auditing CNAME, NS, TXT & TLS Certificates

A marketing team launches a campaign site on a hosted platform, points promo.example.com at it with a CNAME, and the campaign ends. Someone deletes the app on the platform. Nobody deletes the DNS record. Months later, anyone who creates an app with that same name on the platform can serve their own content on promo.example.com, under your domain, with a valid TLS certificate and your parent-domain cookies arriving with every request.

I wrote this with help from AI tools and checked every command and claim myself. I build PixelValut's DNS and network tools, and over nine years of running Laravel and Node.js platforms I have cleaned up more forgotten DNS records than I would like to admit. This post is the audit routine I run on my own domains: how to find dangling CNAME, NS and TXT records, how to confirm them safely, and how to stop creating new ones.

📚 Series Roadmap — Browser-Side Security & Zero-Trust Forensics

What is a subdomain takeover and how do you audit for dangling DNS?

Direct answer: A subdomain takeover happens when a DNS record (usually a CNAME, sometimes NS or MX) still points at a third-party resource you no longer own, and an attacker can claim that resource. Audit by enumerating subdomains from DNS and Certificate Transparency logs, resolving each target, and checking for NXDOMAIN, refused delegations and provider "not found" pages.

Scope note: run everything below only against domains you own or that are explicitly in scope for a bug bounty program. Claiming a resource on someone else's domain, even to prove a point, can break laws and program rules.

Why this is a browser security problem

It is tempting to file this under "DNS hygiene". From the browser's point of view it is much worse, because a taken-over subdomain is a same-site origin:

  • Cookies set with Domain=example.com are sent to every subdomain, including the hijacked one.
  • CORS allowlists written as /^https:\/\/.*\.example\.com$/ will trust the attacker's origin with credentials.
  • CSP rules like script-src 'self' *.example.com let the attacker host scripts your main app will run.
  • OAuth clients with wildcard redirect URIs, which Part 3 warned about, will send authorization codes there.
  • postMessage handlers that check origin.endsWith('.example.com') accept messages from it.

So the blast radius is not "someone defaced a forgotten page". It is "someone can run code inside your trust boundary".

Abandoned glass greenhouse in fog with its doors left open and plants overgrowing the entrance, representing a cloud resource that was deleted while its DNS record still points at it
AI-generated image.

The four record types I audit

Record type Dangling condition What an attacker gains First check Typical cause
CNAME Target is a deleted app, bucket or site on a shared platform Serve content and set cookies on your subdomain dig the target: NXDOMAIN or provider "not found" page Resource deleted, DNS left behind
NS Subdomain delegated to a DNS zone that no longer exists Full control of every record under that subdomain Query the delegated servers directly: REFUSED or SERVFAIL Hosted zone deleted, delegation kept
MX / TXT (SPF) SPF include: or MX points at an expired domain or old vendor Send mail that passes SPF for your domain, or receive it Resolve each include; check the domain's registration Vendor changed, records never cleaned
A / AAAA Points at a released cloud IP Receive traffic for your subdomain if they get that IP Check the IP still belongs to your account Instance or load balancer deleted

CNAME takeovers are the classic case, but NS takeovers are the most severe, because the attacker controls the whole zone, not one host.

Step 1: Enumerate subdomains, including ones you forgot

Your DNS provider's zone file is the obvious source. It is not the only one. Certificate Transparency logs record every publicly trusted certificate, so they often reveal hostnames nobody remembers:

curl -s "https://crt.sh/?q=%25.example.com&output=json" \
  | jq -r '.[].name_value' \
  | tr 'A-Z' 'a-z' | sed 's/^\*\.//' \
  | sort -u > subs.txt
wc -l subs.txt

Merge that with an export of your zone files from every provider you use. In my experience the dangling records live in the zone nobody owns any more: the old registrar's DNS, a second cloud account, a zone a contractor created.

Step 2: Resolve CNAME targets and look for NXDOMAIN

while read -r h; do
  target=$(dig +short CNAME "$h" | tail -n1)
  [ -z "$target" ] && continue
  status=$(dig "$target" A +noall +comments | grep -oE 'status: [A-Z]+' | cut -d' ' -f2)
  echo "$h -> $target [$status]"
done < subs.txt | tee cname_report.txt

grep -E 'NXDOMAIN|SERVFAIL' cname_report.txt

A result like this is a red flag:

promo.example.com -> example-promo.azurewebsites.net. [NXDOMAIN]

For some providers, NXDOMAIN on the target is the whole signal: the name is free to register. On others, the target still resolves to shared infrastructure and the clue is in the HTTP response. A quick way to check one host is the DNS Lookup tool, which shows the full record set and CNAME chain in one view.

Step 3: Fingerprint the HTTP response

for h in $(cut -d' ' -f1 cname_report.txt); do
  body=$(curl -sk -m 10 "https://$h" || curl -s -m 10 "http://$h")
  echo "$body" | grep -qE "There isn't a GitHub Pages site here|The specified bucket does not exist|No such app" \
    && echo "CHECK: $h"
done

Those strings come from the community-maintained can-i-take-over-xyz project, which tracks fingerprints and whether each service is currently exploitable. Its status matters: as I write this it lists AWS S3 and several Azure services as vulnerable, while GitHub Pages, Heroku, Netlify and Vercel are listed as edge cases, because those platforms added domain verification or other mitigations. A fingerprint match is a lead, not a confirmed finding.

Status codes help too. A subdomain that suddenly returns 404 from a platform's default error page, instead of your app's own 404, is worth a look; I use the HTTP Status Checker to see the status and redirect chain without loading the page in a browser.

Step 4: Check NS delegations directly

dig NS dev.example.com +short
# ns-1234.awsdns-26.org.
# ns-567.awsdns-07.net.

dig @ns-1234.awsdns-26.org dev.example.com SOA

If the delegated server answers REFUSED or SERVFAIL instead of an SOA, the zone it is supposed to host does not exist there. That is a lame delegation, and on some providers anyone can create a hosted zone with that name and may land on the same name servers. Major cloud DNS providers have added mitigations over the years, but I treat any lame delegation as a priority fix regardless of whether I think it is exploitable.

Step 5: Audit TXT, SPF and mail records

dig +short TXT example.com
# "v=spf1 include:_spf.google.com include:mail.oldvendor-app.com ~all"

dig +short TXT mail.oldvendor-app.com
dig +short MX example.com

For every domain referenced in an SPF include:, MX record or DMARC rua= address, confirm it still resolves and is still registered. An expired vendor domain in your SPF include means whoever registers it can publish an SPF record that authorizes their own mail servers to send as you. The WHOIS Lookup shows registration and expiry dates for each one. While you are in the TXT records, delete old verification tokens for services you no longer use; they are harmless on their own but they are a map of your past vendors.

Step 6: Read the TLS certificate

openssl s_client -connect promo.example.com:443 -servername promo.example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -enddate -ext subjectAltName

What I look for:

  • A certificate for the provider's own domain (for example a wildcard for the platform) rather than your hostname. The DNS still points there, but your custom domain is no longer attached.
  • A certificate for your hostname that you did not request. Set up Certificate Transparency monitoring for your domains; a new certificate for a subdomain you thought was dead is one of the clearest takeover signals you can get.
  • An expired certificate on a host that should be live, often a sign nobody owns it.

The SSL Checker shows issuer, SAN list and expiry for a single host. A CAA record limiting which CAs may issue for your domain is a good control, but it is not a takeover fix: many hosting platforms issue through a CA you have probably already allowed.

Choosing tooling

Approach What it checks Strength Blind spot
dig + curl scripts (above) CNAME status, fingerprints, NS, TXT Transparent, no dependencies, easy to audit You maintain the fingerprint list
dnsReaper Many providers' takeover signatures, can read zone exports Broad provider coverage Only as current as its signatures
nuclei takeover templates HTTP fingerprints at scale Fast, community updated HTTP only; misses NS and SPF issues
CT log monitoring New certificates for your names Catches takeovers after they happen Detective, not preventive
DNS as code (Terraform, OctoDNS) Records tied to resources in one repo Prevents new dangling records Only covers zones you imported

Preventing it

  1. Delete the DNS record before the resource. Reverse the order and you open a window.
  2. Use platform domain verification where it exists (GitHub verified domains, Azure's asuid TXT records), so other accounts cannot claim your hostnames.
  3. Manage DNS as code next to the infrastructure that the records point at, so deleting a resource fails review if its record remains.
  4. Avoid wildcard trust: scope cookies to the exact host where you can, and write exact-origin CORS and CSP allowlists.
  5. Run the audit on a schedule and alert on any new NXDOMAIN, REFUSED or provider-fingerprint result.

Diagnostic checklist

  1. Export every zone from every DNS provider and registrar you have ever used.
  2. Add hostnames from Certificate Transparency logs.
  3. Resolve every CNAME target; flag NXDOMAIN and SERVFAIL.
  4. Fingerprint HTTP responses and check each match against current provider status.
  5. Query delegated name servers directly for every NS record below the apex.
  6. Resolve and check registration for every SPF include, MX host and DMARC report domain.
  7. Inspect certificates for unexpected issuers, SANs or expiry.
  8. Confirm every A/AAAA IP still belongs to your account.
  9. Search your cookie, CORS, CSP and OAuth configs for wildcard subdomain trust.
  10. Fix by removing or re-claiming the record, then re-run the scan.

FAQ

Is an NXDOMAIN CNAME always exploitable?

No. It depends on whether the provider lets someone else register that exact name. Some do, some reserve names after deletion, and some require domain verification. Check the provider's current status and treat the record as a bug either way, because it is pointing at nothing you control.

Does Cloudflare proxying protect me?

Proxying changes what visitors connect to, but the origin behind it can still be a deleted resource. Audit the origin hostnames in your proxy config the same way you audit public CNAMEs.

Will HSTS stop a takeover?

No. HSTS forces HTTPS, and most hosting platforms will issue a valid certificate for the attacker once they control the resource. HSTS protects against downgrade, not against a legitimate-looking server on your name.

How often should I run this audit?

I run it on a schedule and after every infrastructure teardown, campaign end or vendor change. Those events are when dangling records are born.

I found one in a bug bounty target. How far should I go?

Follow the program's rules. Many ask you to prove control with a harmless file on an unguessable path and nothing more. Never collect cookies, credentials or user data, and report quickly.

Do wildcard DNS records make this worse?

They can. A wildcard CNAME to a shared platform means every unused name under it points somewhere you may not control. Prefer explicit records for each host.

Next in this series, Part 6 covers zero-trust reverse proxy hardening in Nginx: rate limiting, request smuggling guards and mTLS.

Follow & Connect

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.