BreachHorizon
technical

Subdomain takeover: the 60-second check every site owner should run (and how attackers find yours)

Laurens VanhaeckeJun 11, 20268 min readReviewed by Laurens Vanhaecke

Attackers are registering your forgotten subdomains right now — and your DNS is handing them the keys.

Subdomain takeover is one of the most under-patched vulnerabilities in the wild, and it consistently shows up in bug bounty programs, red team engagements, and breach post-mortems. The reason it persists is simple: DNS records outlive the services they point to. A developer spins up a Heroku app, wires staging.yourcompany.com to it, then decommissions the Heroku dyno months later. The CNAME stays in your DNS zone. An attacker claims the free Heroku app name, and suddenly they own staging.yourcompany.com — your brand, your cookies, your users' trust.

This guide skips the theory and gets straight to finding, fixing, and monitoring the issue.

How a subdomain takeover actually happens (the dangling CNAME problem)

The root cause is a dangling DNS record — a CNAME that points to an external hostname on a SaaS platform where the underlying resource no longer exists.

Here is the exact sequence:

  1. Your team creates CNAME staging.yourcompany.com → yourapp.herokuapp.com.
  2. The Heroku app is deleted. yourapp.herokuapp.com now returns a "No such app" error.
  3. The CNAME in your DNS zone is never removed.
  4. An attacker runs automated tooling (subjack, nuclei, dnsx) against your domain, finds the dangling CNAME, and claims yourapp on Heroku.
  5. Heroku's platform routes traffic for yourapp.herokuapp.com to the attacker's app.
  6. Requests to staging.yourcompany.com now land on infrastructure the attacker controls.

The attack works because most SaaS platforms allocate resources by name, not by proof of DNS ownership. The moment the attacker registers the matching app name, the platform happily accepts inbound traffic for it.

This is not a bug in Heroku or GitHub Pages. It is a DNS hygiene failure on your side, which makes it entirely your responsibility to fix.

A less common variant uses A records pointing to cloud IPs that have been released back into a provider's pool. AWS Elastic IPs, Azure Public IPs, and GCP external addresses can all be reassigned. If your DNS points a bare subdomain at a released IP that another customer now holds, the same takeover scenario applies.

The five SaaS platforms most commonly involved (Heroku, GitHub Pages, AWS S3, Azure, Shopify)

Not every platform is equally exploitable. These five generate the most confirmed takeovers in bug bounty disclosures and red team reports.

Heroku is the textbook case. Free-tier app names are first-come-first-served, deletion is instant, and the platform has no domain-ownership verification on the receiving end. Every abandoned *.herokuapp.com CNAME is a live target.

GitHub Pages is close behind. A CNAME pointing to yourorg.github.io on a repository that has been deleted, made private, or had Pages disabled is claimable by anyone who forks or creates a repo under that path. Microsoft acquired GitHub, but the Pages infrastructure has not changed the behavior that makes this exploitable.

AWS S3 static website hosting creates endpoints like yourbucket.s3-website-us-east-1.amazonaws.com. Delete the bucket, leave the CNAME, and the bucket name is free for any AWS account to register. S3 bucket names are global and unique, so the first person to claim it wins.

Azure surfaces this through multiple services: Azure App Service (*.azurewebsites.net), Azure CDN (*.azureedge.net), Azure Static Web Apps, and Traffic Manager profiles. Microsoft's own documentation acknowledges the risk and has added some verification steps for custom domains on App Service, but legacy configurations created before those controls existed are still vulnerable — and Azure CDN endpoints remain exploitable.

Shopify generates storefronts at *.myshopify.com. A CNAME from shop.yourcompany.com to yourstore.myshopify.com becomes dangerous the moment you cancel the Shopify plan and the store name is released back into the pool.

Other platforms that appear regularly in takeover disclosures: Fastly, Pantheon, Ghost (hosted), Surge.sh, Netlify (though Netlify now has drop-in protections), and Zendesk Help Center custom domains.

Running the audit manually with dig + a list of your subdomains

You do not need a paid tool to do an initial sweep. You need three things: a list of your subdomains, dig (or nslookup on Windows), and 60 seconds of focus.

Step 1 — Get your subdomain list.

Pull it from wherever your DNS is managed. If you use Cloudflare, export the zone file from the dashboard (Account → Domain → DNS → Export). If you use Route 53, use the AWS CLI:

aws route53 list-resource-record-sets --hosted-zone-id YOURZONEID \
  --query "ResourceRecordSets[?Type=='CNAME']" --output json

For a faster passive enumeration, run subfinder or amass against your root domain to catch subdomains that exist in certificate transparency logs but may have been dropped from your internal records:

subfinder -d yourcompany.com -silent -o subdomains.txt

Step 2 — Check each CNAME for a dangling target.

while read sub; do
  target=$(dig +short CNAME "$sub")
  if [ -n "$target" ]; then
    response=$(curl -s -o /dev/null -w "%{http_code}" "http://$sub" --max-time 5)
    echo "$sub → $target [$response]"
  fi
done < subdomains.txt

Look for HTTP responses in the 404 range alongside platform-specific error messages. Heroku returns "No such app". GitHub Pages returns a 404 with a GitHub-branded page. AWS S3 returns NoSuchBucket in an XML body. Azure returns a specific "Web App Not Found" page.

Step 3 — Automate the signal with nuclei.

ProjectDiscovery's nuclei ships with a template set specifically for subdomain takeovers:

nuclei -l subdomains.txt -t takeovers/ -silent

The takeovers/ template directory covers 50+ platforms and maps specific fingerprints to confirmed-exploitable states. This is the fastest way to go from a list of 500 subdomains to a prioritized shortlist in under two minutes.

For teams using NinjaOne or similar RMM platforms for endpoint management, the same nuclei scan can be scripted and pushed as a scheduled task across your asset inventory. It is not glamorous, but it beats manual review every quarter.

Real-world impact (credential phishing, cookie theft, brand damage)

The "it's just a staging subdomain" dismissal is the most dangerous thing you can say about this class of vulnerability. Here is what an attacker actually does with a taken-over subdomain.

Credential phishing. The subdomain is yours. The TLS certificate is trivially obtainable via Let's Encrypt once the attacker controls the origin. Users see https://staging.yourcompany.com in the browser bar, a padlock, and a login page that looks identical to yours. Password managers may even auto-fill credentials if the base domain matches a saved entry.

Cookie theft via Same-Site and subdomain scope. Cookies set with Domain=.yourcompany.com are sent to all subdomains, including the attacker's. If your application sets session cookies without the __Host- prefix (which explicitly restricts to the exact origin), a taken-over subdomain receives those cookies on every cross-subdomain request. Session hijacking at scale, no XSS required.

OAuth and SSO abuse. If your identity provider allows redirect URIs scoped to *.yourcompany.com or if you have registered staging.yourcompany.com as a valid redirect URI in your Microsoft Entra ID, Okta, or Auth0 application config, an attacker can initiate a login flow and redirect the authorization code to their controlled endpoint.

Brand damage and SEO poisoning. The attacker can serve malware, disinformation, or spam under your domain. Google's Search Console will index it. Blacklisting services will associate the content with your root domain. Cleaning that up takes months.

These are not theoretical scenarios. Bug bounty programs at Uber, Snapchat, and dozens of Fortune 500 companies have paid out for exactly these chains.

Fixing a vulnerable subdomain in 10 minutes

The fix is always one of two things: remove the DNS record, or reclaim the resource.

Option A — Remove the CNAME (preferred if the service is dead).

Log into your DNS provider. Find the CNAME. Delete it. Done. In Cloudflare this takes under 30 seconds. In Route 53, use the console or:

aws route53 change-resource-record-sets --hosted-zone-id YOURZONEID \
  --change-batch '{
    "Changes": [{
      "Action": "DELETE",
      "ResourceRecordSet": {
        "Name": "staging.yourcompany.com.",
        "Type": "CNAME",
        "TTL": 300,
        "ResourceRecords": [{"Value": "yourapp.herokuapp.com."}]
      }
    }]
  }'

Verify the record is gone with dig staging.yourcompany.com CNAME. If you see NXDOMAIN or no answer section, the record is removed.

Option B — Reclaim the resource (if the subdomain still needs to serve traffic).

Create a new app/bucket/page on the target platform using the exact same name the CNAME references, then immediately configure a redirect or placeholder response. This closes the window while you migrate traffic to a permanent replacement. Do not leave a reclaimed resource idle — that just restores the original state.

For Azure specifically: Microsoft recommends using the asuid TXT record verification for App Service before creating the CNAME. If you are provisioning a new custom domain on Azure App Service, add the TXT record first. That prevents the takeover scenario from opening in the first place.

Review related records. After removing the primary CNAME, check for A records pointing to released cloud IPs and MX records pointing to decommissioned mail services. Dangling MX records enable email spoofing under your domain — a separate but equally damaging issue.

Ongoing monitoring — making sure it doesn't come back

One-time audits decay. Engineers ship new services, DNS records get added in tickets that never reference decommission steps, and the same dangling CNAME problem resurfaces six months later with a different subdomain.

Three layers of control prevent recurrence:

Automated weekly scans. Schedule the nuclei takeover scan in your CI pipeline or as a cron job. Push results to a Slack channel or PagerDuty alert. The scan takes under five minutes against most corporate-sized zone files. If you are using CrowdStrike Falcon Surface or a similar external attack surface management tool, subdomain takeover detection is typically built in — verify it is enabled and the alert routing is configured to reach someone who can act on it.

DNS change alerting. Cloudflare's Audit Log and Route 53 CloudTrail events both record DNS changes. Pipe those events to your SIEM. Alert on new CNAME records pointing to known-risky platforms (herokuapp.com, github.io, s3-website-*, azurewebsites.net, myshopify.com). Alert on CNAME records that are added without a corresponding ticket or change record.

Decommission checklists. The root cause is process, not technology. Every service teardown ticket should have a mandatory step: "Remove DNS records pointing to this service." If your team uses Jira, add it as a required sub-task on any "decommission" issue type. If you use Notion or Linear, add it to your runbook template. The five minutes it takes to add a checklist item will prevent the next incident.

Certificate transparency monitoring. Services like Facebook's crt.sh and CertSpotter index every TLS certificate issued under your domain. If an attacker takes over a subdomain and gets a cert, you will see it. Subscribe to CT log alerts for your root domain — Cloudflare offers this natively; alternatives include crt.sh RSS feeds or the open-source certspotter daemon.

For teams using Microsoft 365 and Azure AD, the Defender for Cloud Apps connector surfaces some of this, but it does not replace external DNS scanning. Defender sees what is inside your tenant; it does not see what is dangling in your public DNS zone pointing outward.


Subdomain takeover is the vulnerability class that should embarrass every organization that gets hit by it, because it is entirely self-inflicted and trivially preventable with basic DNS hygiene. Run the audit, remove the records, automate the monitoring, and add the decommission checklist. That is the entire playbook.

Run the free Exposure Report and validate public-surface findings — including dangling DNS records, open ports, and certificate misconfigurations across your full subdomain inventory.

See what attackers see — before they do.

Run the free passive scan, get a prioritized fix plan, and close the gaps yourself or have us do it for you.