Subdomain takeover: how a forgotten CNAME becomes someone else’s page
Why a DNS record pointing at a cloud service you stopped paying for lets a stranger publish under your domain, how to find such records and how to remove them.
Three years ago, a developer set up docs.yourdomain.com on GitHub Pages. The project ended, the repository was deleted, and the developer left. The DNS record did not. docs.yourdomain.com is still a CNAME to yourcompany.github.io, and GitHub Pages now answers it with “There isn’t a GitHub Pages site here.”
Anyone can now create a repository, enable Pages, add docs.yourdomain.com as its custom domain, and GitHub will serve their content at your address, with a valid certificate. That is a subdomain takeover. It needs no password, no vulnerability in your software and no contact with your servers. It needs a DNS record that nobody remembered to delete.
Why it works
A CNAME record says “for this name, go and look at that other name”. The other name belongs to a provider that hosts many customers’ sites on shared infrastructure and decides which content to serve by reading the host name the browser asked for. When the customer who claimed that host name leaves, the provider forgets the mapping, but your DNS record still routes visitors there. Whoever claims the host name next receives them.
The same pattern exists with A records pointing to an IP address a cloud provider has since reassigned, with NS records delegating a subdomain to name servers that no longer exist, and with CNAMEs pointing to domains whose registration has lapsed. The last one is the worst: anyone can buy the expired domain for a few euros and control every record under your subdomain.
What an attacker does with it
A page on your domain inherits your reputation. The classic uses are a phishing page (“sign in to your account” on support.yourdomain.com), a landing page for malware downloads, and search spam. More damaging and less visible: cookies. If your main site sets cookies on .yourdomain.com rather than on www.yourdomain.com, the attacker’s page receives them. If your content security policy or CORS configuration trusts *.yourdomain.com, the attacker’s page is trusted too.
How these records are found
Nobody attacks a subdomain they do not know about, so the first step, for attacker and defender alike, is a list. The best public source is Certificate Transparency: every certificate issued by a public authority since 2018 is recorded in append-only logs, and the host names on those certificates are searchable. If docs.yourdomain.com ever had an HTTPS certificate, it is in the logs for ever. To see that list for your own domain, with the address and the provider behind each name, open the free DNS and surface map.
Perimetry pulls every host name for your domain from the logs, resolves each one, and follows the CNAME chain. For each endpoint it asks three questions:
- Does the final target still resolve? If a CNAME points to a name that returns NXDOMAIN, the record is dangling.
- Is the final target’s domain registered at all? If not, the subdomain can be taken over by buying a domain.
- Does the target belong to a provider known to serve a recognisable “unclaimed” page, and is it serving that page right now?
The third question uses a list of fingerprints, one per provider: the text GitHub Pages shows for an unclaimed name, the text Heroku shows, Shopify, Zendesk, Azure, Fastly, Ghost, Netlify, Pantheon, Tumblr, Unbounce, Webflow, Wix and others, 26 in total. The fingerprint list is published separately under the MIT licence so that anyone can audit or reuse it.
A record that answers yes to question 1 or 2 is reported as high severity; a positive fingerprint match is also high, with the provider named; a CNAME to a provider whose state cannot be confirmed is reported as medium, with the advice to check.
How to fix a finding
If the subdomain is no longer used: delete the DNS record. That is the whole fix. Removing the record is always safe when nothing points visitors there; the worst case is a broken link somewhere, and a broken link is better than a page controlled by a stranger.
If the subdomain is still needed: re-claim the resource at the provider first (create the Pages site, the app, the store), verify that it serves your content, and only then leave the record in place. Do not leave the record pointing at an unclaimed resource “until we migrate”.
If the target domain is unregistered: delete the record immediately, before someone else notices. Then decide whether the subdomain needs to exist at all.
If an NS delegation is dangling: remove the NS records for the subdomain from the parent zone. A delegation to dead name servers makes the whole subtree unavailable today and claimable tomorrow.
Preventing the next one
- Treat DNS records as part of decommissioning. When a service is cancelled, the ticket is not closed until its DNS records are gone.
- Avoid wildcard records (
*.yourdomain.com) unless you need them; they make every name resolve, including names nobody created, and they hide dangling records from simple checks. - Scope cookies to the exact host (
www.yourdomain.com), not the parent domain. - Do not trust
*.yourdomain.comin CORS or content security policies unless every subdomain is under your control. - Run the check regularly. A record that is fine today becomes dangling the day a subscription lapses. Perimetry’s weekly report lists new subdomains and new takeover findings in its changes section, so a lapsed service shows up within a week.
What the report looks like
Likely subdomain takeover: docs.yourdomain.com — high
docs.yourdomain.comis a CNAME toyourcompany.github.io, which serves GitHub Pages’ “There isn’t a GitHub Pages site here” page. Anyone with a GitHub account can claim this host name. Remove the CNAME record, or create the Pages site and add this custom domain to it.
Each finding names the record, the provider, the evidence, and the one action that removes the risk.