Back to Glossary
high severity · Vulnerability Glossary

Subdomain Takeover

Updated August 24, 2026

4 min read

subdomain takeover
dangling dns record
dangling cname
subdomain takeover detection
subdomain takeover prevention
cname takeover
s3 bucket takeover
dns hygiene

A subdomain takeover happens when a DNS record — almost always a CNAME — still points at a third-party service whose resource was deleted or never claimed. Whoever registers that name on the provider then serves their own content on your hostname. Rated high: the fix is usually one deleted DNS record, but while it dangles the attacker inherits your cookies, your allowlists and your users' trust.

What is a subdomain takeover?

You point a hostname at a SaaS provider with a CNAME:

docs.example.com.  300  IN  CNAME  acme-docs.netlify.app.

Later the site is retired and deleted. The DNS record survives the cleanup — different console, different team, nothing errors. That is a dangling record.

Providers route by hostname (Host header / SNI), and several will hand a name to a new tenant without proof of ownership. An attacker recreates the deleted S3 bucket, claims the free acme-docs subdomain on Netlify or Vercel, or points an unverified GitHub Pages repo at your domain, and the provider serves their content for docs.example.com. A dangling NS delegation hands over a whole child zone. This is OWASP Security Misconfiguration (A02:2025, formerly A05:2021); the root cause is inventory drift, not a code bug.

Why it's a risk / how it's exploited

The browser treats the attacker's page as part of your site:

  • Cookies. Anything scoped Domain=example.com — session IDs, CSRF tokens — reaches every subdomain, so cookie scoping becomes session theft.
  • Same-site assumptions. SameSite is evaluated on the registrable domain, so the hijacked host still counts as same-site and SameSite protections never fire.
  • Allowlists. CORS, CSP frame-ancestors and OAuth redirect_uri rules written as *.example.com now include the attacker; chain an open redirect and token theft is short work.
  • Trust. Phishing from a hostname staff recognise — and since the attacker controls the content, they pass HTTP-01 validation and get a real cert, so there is no certificate warning.

Honest precondition: it only lands if the provider will give that hostname to a stranger. GitHub Pages blocks it once you verify the domain (a _github-pages-challenge-<owner> TXT record), and Azure App Service will not serve a custom hostname without an asuid TXT record in your zone. Where verification is enforced a dangling record is hygiene debt; S3 buckets, claimable by name alone, are the opposite.

How to detect it

Does the record still resolve, and does the provider answer as unclaimed? The proof is in the HTTP body, not in DNS — most providers wildcard their zone, so acme-docs.netlify.app resolves whether or not anyone owns it.

dig +short CNAME docs.example.com          # acme-docs.netlify.app.
curl -s https://docs.example.com/ | head -c 300

Vulnerable is a live record plus a provider-generic error naming no tenant: <Code>NoSuchBucket</Code> (S3), There isn't a GitHub Pages site here., No such app (Heroku), DEPLOYMENT_NOT_FOUND (Vercel). Safe is your own content. Read NXDOMAIN carefully: no record at all is fine, but a CNAME whose chain ends in NXDOMAIN means the target is gone — Azure's signature, since deleting an App Service frees <name>.azurewebsites.net. Sweep the whole zone, enumerated from the zone export plus certificate-transparency logs.

How to fix it

Order of operations is the whole fix. Decommissioning: delete the DNS record, wait out the TTL, then release the resource. Already dangling: reclaim the name at the provider first to deny the attacker, then delete the record and release. Keep DNS and the resource in one IaC state so cleanup cannot drift apart:

resource "aws_route53_record" "assets" {
  zone_id = var.zone_id
  name    = "assets.example.com"
  type    = "CNAME"
  ttl     = 300 # short TTL, short cleanup window
  # Referencing the bucket makes the dependency real: destroy drops the record first.
  records = [aws_s3_bucket_website_configuration.assets.website_endpoint]
}

Where the provider offers it, pin the hostname to your tenant with its ownership record, and keep session cookies host-only with Set-Cookie: __Host-session=...; Path=/; Secure; HttpOnly; SameSite=Lax. The __Host- prefix forbids a Domain attribute, so that cookie never reaches a hijacked subdomain.

Caveats: CAA does not prevent takeover — it limits which CA may issue, and an attacker using your allowlisted CA still gets a cert. A wildcard *.example.com defeats provider verification entirely. __Host- cookies break cross-subdomain SSO. And a deleted record keeps resolving from caches until its TTL expires.

FAQ

Is a dangling CNAME a real finding if nobody can claim the target? Severity drops to low when the provider enforces domain verification: the exploit needs a claimable resource. But policies and account ownership change, and the record outlives whoever knew that detail. Fix it as hygiene; rate it high only once you have proved the claim is possible.

Can an attacker get a certificate for example.com itself? No. HTTP-01 and TLS-ALPN-01 validation cover only the exact hostname they control, and a wildcard needs DNS-01 against your zone. A trusted cert for sub.example.com plus parent-domain cookies is already enough for phishing and session theft.


Dangling records come from ordinary cleanup drift, so the defence is continuous inventory. Run a free website vulnerability scan with Exploita to enumerate your subdomains, flag dangling DNS records with a verified proof of concept and re-check every scan.