Dangling CNAMEs and subdomain takeover: how to find them
· subdomain-takeover · dns · cname · attack-surface · security
subdomain-takeoverdnscnameattack-surfacesecurityA 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.comwith 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.comare 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.comnow trusts the attacker.
How to find dangling records
Three steps:
- Enumerate subdomains. Pull your zone file, scrape Certificate Transparency logs (
crt.sh), and run passive DNS — you want every host, including the forgotten ones. - Resolve the chain. For each host, follow the
CNAMEto its final target and note the service it points at. - 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
- Security headers: the ones that actually matter
- Reading a TLS certificate from the command line
- RFC 1034 — Domain Names: Concepts and Facilities (the
CNAMEsemantics this all rests on)