Subdomain takeover: how it happens, how to detect it, how to fix it
A dangling DNS record pointing to a deprovisioned service lets an attacker claim the resource and serve content from your subdomain.
Typical severity: High · Checked by NovaPentest module: Subdomain Takeover
What it is
Subdomain takeover happens when a DNS record - most often a CNAME - points to an external service (GitHub Pages, Heroku, an S3 bucket, Azure, Netlify, Fastly, Shopify, Zendesk, and similar) that no longer hosts anything for you. The DNS entry still resolves and still delegates the subdomain's traffic to that provider, but the underlying resource has been deleted, its trial expired, or it was never claimed. Because providers assign resources on a first-come basis, anyone who registers the same bucket name, app name, or Pages repository now controls what visitors of your subdomain see.
The subdomain still carries your organization's name and, crucially, your domain's trust: valid TLS can often be obtained, cookies scoped to the parent domain may be readable, and content-security or single-sign-on flows that trust the host can be subverted. The root cause is a lifecycle gap - a service is decommissioned but its DNS record is left behind ("dangling"). It maps to OWASP Security Misconfiguration and CWE-350.
The two dangerous states are: (1) the target host no longer resolves at all (NXDOMAIN) while another record still points to it, and (2) the CNAME points at a known provider whose resource is unclaimed. Both are exploitable; the difference is only how quickly an attacker can grab the name.
How it is exploited
An attacker enumerates an organization's subdomains (certificate transparency logs, DNS brute forcing, public datasets) and checks each one's CNAME target. When a target points to a provider that returns a "resource not found" fingerprint - for example an S3 "NoSuchBucket" message or GitHub's "There isn't a GitHub Pages site here" page - the name is up for grabs. The attacker signs up for that provider and registers the exact bucket, app, or repository the CNAME expects, then serves their own content.
From that point the malicious page loads under your subdomain, so it inherits the domain's reputation. Typical abuse: convincing phishing hosted on a name victims already trust; reading or setting cookies scoped to *.example.com to hijack sessions; bypassing a Content-Security-Policy that allowlists the subdomain; abusing OAuth or redirect allowlists that trust the host; or obtaining a valid certificate to make the fake site look legitimate. Because control is total, the attacker can also load scripts into pages that frame or fetch the subdomain.
Impact ranges from brand damage and phishing to full session hijacking and, where SSO or API trust is involved, account takeover. The attack needs no exploit code - only that the dangling record and the free provider slot line up.
How to detect it
NovaPentest's Subdomain Takeover module works from DNS first. It reads the real CNAME chain for the hostname, following up to five hops. If the hostname has no CNAME and does not resolve at all (NXDOMAIN), it reports a High finding, because any record elsewhere that still points at this name is dangling.
When there is a CNAME, it checks whether the name the record points to still exists. A CNAME whose target returns NXDOMAIN is the classic takeover precondition: the module reports it as Critical when the target belongs to a provider known to allow re-registration of released names (for example github.io, herokuapp.com, S3, Azure App Service or Netlify), and High otherwise.
If the target does exist and belongs to one of those providers, the module fetches the page and looks for the provider's own "this resource is unclaimed" message, such as "There isn't a GitHub Pages site here" or "NoSuchBucket". Only a distinctive message like that produces a Critical finding. Generic text such as "Not Found" is not treated as proof, because ordinary 404 pages contain it. A record that points at a takeover-prone provider but shows no sign of being unclaimed is reported as a Low ownership-review item.
The module checks the target you register, not every subdomain in your zone, so it does not enumerate your DNS.
To verify manually, run `dig CNAME sub.example.com +short` to see where the record points, then `dig A <target> +short` to confirm the target still resolves. If it does, open the hostname in a browser and compare the page against the provider's documented unclaimed-resource message. Repeat this for every CNAME in your zone, not just the ones you remember creating.
How to fix it
Fix dangling records at the source and prevent them with process.
1. Inventory every DNS record and map each CNAME/ALIAS to a live, owned resource. Any target that returns NXDOMAIN or a provider "not found" page is dangling - delete the DNS record immediately; that alone closes the takeover.
2. Enforce order of operations when decommissioning: remove or repoint the DNS record first, then delete the cloud resource - never the reverse. The gap between deleting the resource and deleting the record is exactly the exploitable window.
3. Claim before you point: only create a CNAME to a provider after the bucket, app, or Pages site actually exists and is owned by you, so the name is never briefly free.
4. Prefer providers that bind a resource to a verification token or require domain-ownership proof, which stops a stranger from claiming the name even if the record lingers.
5. Automate continuous monitoring: schedule recurring resolution and fingerprint checks across all subdomains (the same checks a scanner performs) and alert on any NXDOMAIN or unclaimed-provider response.
6. Reduce sprawl: retire wildcard and stale subdomains, and review certificate-transparency logs for names you no longer use.
Prioritize confirmed and NXDOMAIN cases first (immediate deletion), then audit at-risk providers still serving content for continued ownership.
Check your own site
Start with the free passive scan: no account needed, and you get an A to F grade covering headers, TLS, cookies, DNS and other configuration a visitor can observe. It does not send attack payloads, so it will not confirm every class of flaw. The full scan, available with a free account, runs the active checks, including the one described on this page.