Subdomain Takeover: How Dangling DNS Records Become Account Hijacks

Subdomain takeover is a vulnerability where a DNS record points to a cloud resource that no longer exists — allowing an attacker to claim that resource and serve content from your domain. This guide covers how it works, how to find dangling records in your own infrastructure, and how to prevent it.

What Is Subdomain Takeover?

Subdomain takeover occurs when a DNS record — typically a CNAME — points to a cloud service or platform that the original owner no longer controls. An attacker claims that service endpoint and starts receiving traffic intended for the original subdomain. From the visitor’s perspective, they’ve navigated to a legitimate-looking URL within your domain. From the attacker’s perspective, they control a page served from api.yourcompany.com or staging.yourcompany.com.

The DNS record is the vulnerability. The cloud service (GitHub Pages, Azure App Service, AWS S3, Heroku, Fastly, etc.) is the attack surface.

How a Dangling CNAME Is Created

The lifecycle of a dangling record looks like this:

  1. A developer deploys staging.example.com to Heroku, pointing the CNAME at example-staging.herokuapp.com
  2. The project is decommissioned. The Heroku app is deleted.
  3. The DNS record is not cleaned up — it still points to example-staging.herokuapp.com
  4. That Heroku subdomain is now available for anyone to register
  5. An attacker creates a Heroku app with the name example-staging
  6. Requests to staging.example.com now resolve to the attacker’s Heroku app

The attack works because:

  • The DNS CNAME is authoritative — it points to a resource under your domain
  • Cookies scoped to *.example.com are sent to the subdomain
  • Browsers trust the origin as part of the example.com domain
  • TLS certificates can be issued for the subdomain (Let’s Encrypt will sign a cert for a domain you control in DNS terms)

The Security Impact

If your application sets session cookies with Domain=.example.com, those cookies are sent to every subdomain — including a taken-over one. An attacker serving a page from staging.example.com receives every cookie scoped to the parent domain when a user visits their page.

This applies to authentication tokens, CSRF tokens, and any other sensitive values in *.example.com cookies.

Credential Phishing

A taken-over subdomain serves content from your domain. An attacker can host a convincing login page at login.example.com, capturing credentials that users submit believing they’re on a legitimate company property.

OAuth Redirect URI Abuse

If dev.example.com is a registered OAuth redirect URI for your application and an attacker takes it over, they can initiate an OAuth flow and receive the authorization code at their endpoint. The user authorises the real application, the code arrives at the attacker’s server.

Content Security Policy Bypass

If your CSP includes *.example.com as a trusted origin (common in multi-subdomain applications), a taken-over subdomain can load scripts that execute in the context of that trusted origin when navigated to directly.

Reputation and Trust Abuse

Beyond technical exploitation, a taken-over subdomain gives an attacker a high-trust delivery channel: sending emails that appear to originate from your domain, hosting phishing pages that pass visual inspection, distributing malicious files with your brand.

Platforms Commonly Involved

Almost every major cloud and PaaS platform has seen subdomain takeover incidents:

PlatformDangling CNAME PatternClaim Mechanism
GitHub Pages*.github.ioCreate repository matching the name
Azure App Service*.azurewebsites.netCreate App Service with the name
Azure Traffic Manager*.trafficmanager.netRegister the profile
AWS S3*.s3.amazonaws.com or s3-website-*.amazonaws.comCreate bucket matching the name
Heroku*.herokuapp.comCreate app with the name
Fastly*.fastly.netCreate service pointing to the CNAME
Shopify*.myshopify.comRegister the shop name
Zendesk*.zendesk.comCreate account with the subdomain

The pattern is consistent: a cloud resource is associated with a name, the name is released, and that name becomes claimable by anyone.

Finding Dangling Records

Manual DNS Audit

For each CNAME record in your zone, resolve the target and check whether it returns a “service not found” response from the platform:

# Check for common dangling indicators
host staging.example.com
# If CNAME target resolves but returns 404 with platform error: potential takeover

# Check specific platform signatures
curl -s https://staging.example.com | grep -i "there isn't a github pages site here"
curl -s https://staging.example.com | grep -i "no such app"
curl -s https://staging.example.com | grep -i "the specified bucket does not exist"

Automated Scanning with subjack

subjack checks a list of subdomains against known fingerprints for vulnerable platforms:

go install github.com/haccer/subjack@latest

# Generate subdomain list from your DNS zone export
# Run subjack against them
subjack -w subdomains.txt -t 100 -timeout 30 -ssl -c fingerprints.json -v

subjack’s fingerprint database covers the “not found” response strings for most major platforms and reports which subdomains match a takeover-vulnerable pattern.

Alternative: can-i-take-over-xyz

The can-i-take-over-xyz repository maintains a community-updated list of which platforms are vulnerable to subdomain takeover and what the fingerprints are. Use it alongside your scan tooling.

DNS Zone Export Approach

Export your full DNS zone and identify every CNAME record:

# If using Route 53
aws route53 list-resource-record-sets --hosted-zone-id Z1234567890 \
  | jq '.ResourceRecordSets[] | select(.Type == "CNAME") | {Name: .Name, Value: .ResourceRecords[0].Value}'

# Check each CNAME target against platform fingerprints

For large zones, automate the resolution check and fingerprint matching. The key signal is a CNAME target that resolves (the DNS is valid) but returns a platform-specific “this resource doesn’t exist” page.

CI/CD Integration

Run subdomain takeover scanning as part of your security pipeline. On every DNS change (pull request against your zone file, Terraform plan for Route 53), check whether newly added CNAMEs point to live resources and whether existing CNAMEs being removed would leave dangling records elsewhere.

Prevention

1. Remove DNS Records When Decommissioning Services

The root cause is almost always operational: the cloud resource is deleted but the DNS record isn’t. Make DNS cleanup a required step in your service decommissioning runbook. If you use Terraform for infrastructure, tie the DNS record resource to the cloud resource — when the app is destroyed, the DNS record is too.

# Correct: DNS record depends on the App Service
resource "azurerm_app_service" "staging" {
  name = "example-staging"
  # ...
}

resource "azurerm_dns_cname_record" "staging" {
  name    = "staging"
  zone_name = azurerm_dns_zone.main.name
  record  = azurerm_app_service.staging.default_site_hostname
  # When staging app service is destroyed, this record goes with it
  depends_on = [azurerm_app_service.staging]
}

2. Use Custom Domain Verification Where Available

Several platforms require domain ownership verification before a custom domain can be pointed at them. Azure App Service’s asuid TXT record verification, for example, means a CNAME pointing to an Azure App Service subdomain can only be claimed by someone who can add the verification TXT record to the DNS zone — which an attacker cannot do without zone access.

Check whether your chosen PaaS supports verification tokens and enable them. GitHub Pages requires repo ownership; some platforms tie the custom domain to an account.

3. Use NS Delegation Records Carefully

If you delegate a subdomain to a third-party’s nameservers (common with CDN providers), ensure those nameservers are authoritative for resources you control. An NS delegation that points to a provider’s nameserver without an associated resource can sometimes be hijacked at the provider level.

Avoid setting cookies with Domain=.example.com unless you require cross-subdomain sharing. When cookies are scoped to a specific subdomain (Domain=app.example.com), a taken-over subdomain does not receive them.

// Avoid: sends cookie to all subdomains
res.cookie('session', token, { domain: '.example.com' });

// Prefer: scoped to specific subdomain
res.cookie('session', token, { domain: 'app.example.com' });

5. OAuth Redirect URI Hygiene

Audit registered OAuth redirect URIs quarterly. Remove any URIs pointing to decommissioned environments. If a redirect URI includes a subdomain that’s been taken over, attackers can intercept authorization codes.

6. Reserve Common Subdomain Names on Target Platforms

For high-risk platforms where you have an account, consider pre-registering subdomains that correspond to your DNS entries even after decommissioning the resource they served. On GitHub Pages, for example, keeping an empty repository named example-staging under your organization prevents an attacker from registering it.

7. Continuous Monitoring

Dangling records can appear whenever infrastructure changes. Implement continuous DNS monitoring:

  • Export your DNS zone weekly and diff against the previous week
  • Run subjack or equivalent against the full CNAME list on each diff
  • Alert on new CNAMEs pointing to platforms known to be takeover-vulnerable
  • Monitor for unexpected content changes on your subdomain estate

Bug Bounty and Responsible Disclosure

Subdomain takeover is one of the most commonly reported valid findings in bug bounty programs. If you’re running a program, ensure your scope explicitly includes subdomain takeover on non-production subdomains — the risk is real even if the subdomain says “staging.” If you’re a researcher who discovers a dangling record on a company’s domain, report it responsibly — the company will thank you, and most programs pay for this class of finding.

Summary

Subdomain takeover is a low-complexity, high-impact vulnerability that results from an operational gap: cloud resources are decommissioned faster than DNS records are cleaned up. The fix is operational — audit your CNAME records, remove the ones that point to released resources, and make DNS cleanup a required step in your decommissioning process. Continuous scanning catches the records that slip through.