Dr.Who
← blog

Dangling CNAMEs and subdomain takeover: how to find them

· subdomain-takeover · dns · cname · attack-surface · security

subdomain-takeoverdnscnameattack-surfacesecurity

A subdomain takeover happens when a DNS record — usually a CNAME — still points at a third-party service (an S3 bucket, GitHub Pages site, Heroku app, Azure resource, Fastly service) that has been deprovisioned or was never claimed. Because the record is "dangling" — the DNS still resolves somewhere that is no longer yours — an attacker who registers that same resource gets to serve their own content on your subdomain. To find them you enumerate your subdomains, resolve each CNAME, and flag any whose target returns a service-specific "unclaimed" fingerprint.

What "dangling" actually means

Dangling does not mean the record is broken or fails to resolve. It means it resolves to a backing resource that no longer belongs to you. You spun up status.example.com as a CNAME to a Heroku app, killed the app, and forgot the DNS record. The CNAME is still live and still answers — Heroku just no longer has anything bound to that hostname. The slot is open, and anyone can claim it.

dig +short CNAME status.example.com
status-example.herokuapp.com.

That target resolving to an unclaimed app is the whole vulnerability.

Why takeover is worse than it sounds

This is not a defaced marketing page. The attacker now controls a hostname inside your domain, and a lot of trust is scoped to the domain, not the host:

  • Phishing that passes every smell test. login.example.com with a valid TLS cert (most takeover targets let you issue one) is far more convincing than any look-alike domain.
  • Cookie theft. Cookies scoped with Domain=example.com are sent to every subdomain. Content on a hijacked subdomain can read them.
  • Trust abuse. Any CORS allowlist, CSP script-src, or OAuth redirect URI that trusts *.example.com now trusts the attacker.

How to find dangling records

Three steps:

  1. Enumerate subdomains. Pull your zone file, scrape Certificate Transparency logs (crt.sh), and run passive DNS — you want every host, including the forgotten ones.
  2. Resolve the chain. For each host, follow the CNAME to its final target and note the service it points at.
  3. Fingerprint the response. Fetch the target over HTTP and match service-specific "unclaimed" signatures:
curl -sI https://docs.example.com | head -1
HTTP/2 404

curl -s https://docs.example.com | grep -i "github pages"
There isn't a GitHub Pages site here.

Other tells: NoSuchBucket from S3, NoSuchDistribution from CloudFront, "No such app" from Heroku, the default "404 Not Found" Fastly page, or an Azure Web App - Unavailable. A CNAME plus a known unclaimed fingerprint equals a confirmed takeover candidate.

Fix it, then prevent it

Fix: for each dangling record, either delete the DNS record or reclaim the backing resource (re-create the bucket/app/site under the exact name). Deleting is usually right — if the service is gone, the record should be too.

Prevent: always delete the DNS record before you tear down the backing service, never after. That ordering closes the window entirely. Then audit periodically — provisioning is decentralized, so new dangles appear the moment someone offboards a side project.

Scan your subdomains for dangling records →

Further reading