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:
- A developer deploys
staging.example.comto Heroku, pointing the CNAME atexample-staging.herokuapp.com - The project is decommissioned. The Heroku app is deleted.
- The DNS record is not cleaned up — it still points to
example-staging.herokuapp.com - That Heroku subdomain is now available for anyone to register
- An attacker creates a Heroku app with the name
example-staging - Requests to
staging.example.comnow 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.comare 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
Cookie Theft
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:
| Platform | Dangling CNAME Pattern | Claim Mechanism |
|---|---|---|
| GitHub Pages | *.github.io | Create repository matching the name |
| Azure App Service | *.azurewebsites.net | Create App Service with the name |
| Azure Traffic Manager | *.trafficmanager.net | Register the profile |
| AWS S3 | *.s3.amazonaws.com or s3-website-*.amazonaws.com | Create bucket matching the name |
| Heroku | *.herokuapp.com | Create app with the name |
| Fastly | *.fastly.net | Create service pointing to the CNAME |
| Shopify | *.myshopify.com | Register the shop name |
| Zendesk | *.zendesk.com | Create 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.
4. Cookie Domain Scoping
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.