Subdomain takeover: how it works, how testers prove it, how to fix dangling DNS

A subdomain takeover happens when a DNS record points to a service you no longer use, and someone else claims that service name. The attacker then serves content on your subdomain. Prevent it by removing the DNS record before the resource, keeping a DNS inventory, using domain verification, and scanning for dangling records.

A subdomain takeover is an attack in which someone claims control of a subdomain of your domain because its DNS record points to a service you no longer use. OWASP's testing guide names two conditions: a DNS record points to a non-existing or non-active resource, and the hosting provider does not handle ownership verification properly. This page also covers dangling DNS, the record state that makes it possible.

Only test systems you own or have written authorization to test. Every example here uses a fictional app on example.com.

What is a subdomain takeover?#

A takeover starts when a team deletes a cloud resource and forgets its DNS record. Microsoft describes the flow in three steps: you create a resource and point a CNAME at it, you delete the resource but leave the record, and an attacker creates a resource with the same name and receives your traffic. The record is "dangling".

CNAME records are the usual case, because the subdomain follows the target. OWASP's guide also lists A, MX, NS and TXT records. An NS takeover is rarer, and OWASP says it has the highest impact because it can give control of a whole DNS zone. A CNAME that points to an expired domain is a second route: whoever registers that domain controls the subdomain.

Why does a subdomain takeover matter?#

Browsers treat a taken-over subdomain as part of your site. That is why the damage reaches beyond a defaced page.

  • Cookies. MDN says a cookie with a Domain attribute is available to that domain and all its subdomains. A cookie set for .example.com goes to the attacker's server when the victim visits the taken-over name. Microsoft notes this can include secure cookies.
  • Certificates. Microsoft says the attacker can get a valid certificate for the hijacked name, so the padlock does not help.
  • Trust lists. OWASP lists CSP bypass and OAuth and SSO abuse among the impacts. A CORS rule that trusts every subdomain extends the damage.
  • Email. MX and SPF records can let the attacker receive or send mail for the name.

How the attack works#

The attacker finds a subdomain whose CNAME points to a deleted resource, creates a resource with the same name on the same provider, and uploads content. A tester shows the first half and stops.

Demonstration only: Harbor Notes, a fictional app

This is a demonstration against a fictional app. No real customer system was tested. Harbor Notes ran a campaign page at promo.example.com, hosted on a fictional provider. The team deleted the hosted site in March but left the DNS record.

$ dig +short CNAME promo.example.com
harbor-promo.pages.example.net.

$ dig +short A harbor-promo.pages.example.net

$ curl -s -o /dev/null -w "%{http_code}\n" https://promo.example.com/ 404 $ curl -s https://promo.example.com/ | head -n 2 <h1>There is no site here</h1>

The CNAME exists, the target resolves to nothing and the page returns the provider's "unclaimed" message. The tester reports a takeover candidate. The next step needs the owner's written permission and the provider's rules.

The attacker's version adds a claim step. Whether the claim works depends on the provider, which is the point of the next table.

Which providers are vulnerable? Fingerprints by service#

The table uses the can-i-take-over-xyz project, which tracks each service and its status. As of 9 November 2026 the project lists these entries. It states that its authors take no responsibility for correctness and that the data changes, so check the live table and the linked issue for the service before you report anything.

Service Status in the project Fingerprint it lists
AWS S3 Vulnerable (verified by its CI) "The specified bucket does not exist"
Microsoft Azure Vulnerable (verified by its CI) NXDOMAIN on the target name
GitHub Pages Edge case "There isn't a GitHub Pages site here."
Heroku Edge case "No such app"
Shopify Edge case "Sorry, this shop is currently unavailable."
Netlify Edge case "Not Found - Request ID:"
Vercel Edge case "DEPLOYMENT_NOT_FOUND."
Fastly Not vulnerable "Fastly error: unknown domain:"
Zendesk Not vulnerable "Help Center Closed"

An error page is a lead. An "edge case" means the claim works only under conditions, such as an account that has not verified the domain, and the project's issue thread for each service explains them. A "not vulnerable" entry still means the record is dangling and should be removed.

Azure deserves a note. Microsoft says that when an asuid.{subdomain} TXT record with the domain verification ID exists, no other subscription can validate the custom domain. Those records do not stop someone from creating an App Service with the name in your CNAME, but without proof of domain ownership the attacker cannot receive traffic or control content.

Real incidents and research#

Two documented cases show the same pattern at scale. Each is from the publisher named.

Case Date What the source says
SubdoMailing (Guardio Labs) 26 Feb 2024 More than 8,000 domains compromised, from old subdomains whose CNAME pointed to expired domains. The attacker re-registered the domains, so SPF policies authorized the attacker's mail servers. One example sent mail that appeared to come from a msn.com subdomain, and it passed SPF, DKIM and DMARC.
watchTowr, abandoned S3 buckets 5 Feb 2025 (The Record) About 150 abandoned buckets that software and infrastructure still referenced were re-registered, and they received over 8 million HTTP requests in two months, including requests for updates and VM images. AWS blocked those bucket names from being re-created.

The S3 case is a close relative of subdomain takeover: a name was released while other systems still pointed to it. The fix is the same: find the references before you release the name.

How penetration testers detect and prove it#

OWASP's test method is to enumerate domains, past and present, and find forgotten or misconfigured ones. With dig, look for NXDOMAIN, SERVFAIL, REFUSED or no servers reached.

  1. Enumerate subdomains from the zone file if you have it (gray-box) or from wordlists, search engines and certificate logs (black-box). OWASP names dig, dnsrecon, recon-ng, theHarvester, Sublist3r and Amass.
  2. Resolve each name's CNAME. Keep the names whose target does not resolve, or whose page shows a provider fingerprint.
  3. Match the provider and look up its status in the project table above.
  4. For NS records, OWASP's check is whether the nameserver's parent domain can be registered. If it is available, the subdomain is vulnerable.
  5. Run controls. Check that the name is not intentionally parked, that the record is not behind a CDN that returns the same error for all misses, and that the owner is the same organization.
  6. Decide whether a claim is in scope. Claiming a name on a third-party provider creates a resource in someone else's system. Do it only with written authorization from the owner and within the provider's terms.
  7. If you claim, serve one unique harmless text file at a non-root path, such as a file whose name is a random value and whose content is a hash and the date. Do not change the home page. Record the request that retrieved the file, then release the name.

A read-only script helps in the first two steps. It makes DNS and HTTP requests only to names you list.

#!/usr/bin/env bash
# usage: ./dangling.sh subdomains.txt   (names you own or may test)
while read -r name; do
  target=$(dig +short CNAME "$name" | head -n 1)
  [ -z "$target" ] && continue
  resolves=$(dig +short A "$target" | head -n 1)
  code=$(curl -s -o /dev/null -m 10 -w "%{http_code}" "https://$name/")
  echo "$name -> $target resolves=${resolves:-NO} http=$code"
done < "$1"

Names that print resolves=NO or a 404 get a manual check. Tools such as nuclei and dnsReaper automate fingerprint matching, and OWASP's cheat sheet recommends them with can-i-take-over-xyz for scanning.

Evidence is labeled "Observed" for the DNS answers and responses, and "Browser-observed" if a page or cookie behavior is shown in Chromium. A dangling record with no way to claim the name is reported as a record to remove, not as a confirmed takeover. If the tester never claimed the name, the report says the claim step was not performed.

How to prevent subdomain takeover#

Remove the DNS record before you delete the resource. OWASP's lifecycle step is to put up a redirect or maintenance page, change or delete the record, wait at least the TTL, then remove the cloud resource. Deleting the resource first is the typical error.

The prevention list follows OWASP's cheat sheet and Microsoft's guidance.

  1. Keep a DNS inventory that maps each record to its target, owning team and reason.
  2. Scan for dangling records on a schedule and in the teardown step of your deployment pipeline.
  3. Use domain verification where the provider offers it, such as the Azure asuid TXT record, and keep the verification record after decommissioning.
  4. Use alias records where they exist. Microsoft says Azure DNS alias records tie the record to the resource, and an alias to a deleted resource becomes an empty record set. The list covers Front Door, Traffic Manager, CDN endpoints and public IPs.
  5. Keep wildcard records narrow and watch Certificate Transparency logs.
  6. Limit the blast radius. Avoid parent-domain cookies and use host-only cookies; the __Host- prefix forbids a Domain attribute. Use exact-match OAuth redirect URIs and explicit CSP and CORS lists.

In Terraform, the record and the resource can be removed in one change. When the record references an attribute of the resource, Terraform destroys the record first because the record depends on the resource.

resource "aws_route53_record" "promo" {
  zone_id = var.zone_id
  name    = "promo.example.com"
  type    = "CNAME"
  ttl     = 300
  records = [azurerm_linux_web_app.promo.default_hostname]
}

If you delete resources outside Terraform, remove the record first and check it is gone before you delete the resource.

aws route53 change-resource-record-sets --hosted-zone-id Z0EXAMPLE \
  --change-batch file://delete-promo.json
dig +short CNAME promo.example.com   # expect empty before the resource is deleted

For a cookie that must stay on one host, set it without a Domain attribute:

Set-Cookie: __Host-session=<value>; Secure; HttpOnly; Path=/; SameSite=Lax

DNS zone review checklist#

  • Does every CNAME target resolve to a resource your organization owns?
  • Does every record have an owner and a reason in the inventory?
  • Do names that return a provider "not found" page appear in a scan report?
  • Do NS records point to nameservers whose domains you control?
  • Are MX and SPF include: entries for services still in use?
  • Do cookies use host-only scope where possible?
  • Do OAuth redirect URIs, CSP and CORS lists name exact hosts, not *.example.com?
  • Does the decommissioning checklist remove the record first and run a final scan?
  • Do Certificate Transparency logs show certificates only for names you issued?

When to get a professional test#

A DNS zone review is tedious to do by hand, and the impact depends on cookies, OAuth and trust lists in your applications. A tester needs written scope that names the domains and says whether claiming a third-party resource is allowed. Excession Labs includes subdomain checks in a web application penetration test when the zone is in scope, and reports the controls that held next to the findings.

Questions

What is dangling DNS?

A dangling DNS record points to a resource that no longer exists, such as a deleted cloud app or an expired domain. Microsoft calls these entries dangling DNS. The record stays advertised as an active name, so anyone who can register the missing target can receive its traffic.

Can a wildcard DNS record cause a takeover?

It can widen the risk. OWASP's prevention cheat sheet advises keeping wildcards narrow, adding a hostname allowlist at the proxy and watching Certificate Transparency logs. A wildcard that points at a service you no longer control makes every name under it claimable.

Are deleted S3 buckets a takeover risk?

Yes. The can-i-take-over-xyz project lists AWS S3 as vulnerable, with the error text 'The specified bucket does not exist'. In 2025 watchTowr registered about 150 abandoned buckets that software and infrastructure still referenced and received over 8 million requests in two months.

Do HTTPS certificates protect users from a takeover?

No. Microsoft's documentation says a threat actor can use the hijacked subdomain to apply for and receive a valid SSL certificate, which can make the site look more legitimate. Parent-domain cookies are also exposed. Use host-only cookies to limit that.

Which tools find subdomain takeovers?

OWASP's cheat sheet names dnsReaper, nuclei and can-i-take-over-xyz for dangling record detection. OWASP's testing guide lists dig, dnsrecon, recon-ng, theHarvester, Sublist3r and Amass for enumeration. Run them only against domains you own or have written authorization to test.

Sources

  1. OWASP Cheat Sheet Series: Subdomain Takeover Prevention Cheat Sheet (checked 9 November 2026)
  2. OWASP WSTG-CONF-10: Test for Subdomain Takeover (v4.2) (checked 9 November 2026)
  3. Microsoft Learn: Prevent dangling DNS entries and avoid subdomain takeover (checked 9 November 2026)
  4. can-i-take-over-xyz (EdOverflow), service status and fingerprints (checked 9 November 2026)
  5. MDN: Set-Cookie header (Domain attribute and __Host- prefix) (checked 9 November 2026)
  6. Guardio Labs: SubdoMailing, thousands of hijacked major-brand subdomains (26 February 2024) (checked 9 November 2026)
  7. The Record: Researchers warn of risks tied to abandoned cloud storage buckets (5 February 2025) (checked 9 November 2026)