Subdomain takeover is one of the most underappreciated vulnerabilities in the external attack surface. It requires no exploitation skill to execute, it's trivially automatable, and organizations create the conditions for it themselves — every time they deprovision a cloud service without cleaning up DNS.
What Is Subdomain Takeover?
When you deploy a service to a third-party platform (GitHub Pages, Heroku, AWS Elastic Beanstalk, Azure CDN, Fastly, etc.), you typically create a CNAME record pointing your subdomain to the platform's infrastructure:
staging.yourcompany.com → CNAME → yourcompany.eu.ngrok.io
When you decommission that service, you delete the Heroku/GitHub/Azure resource. But if you forget to delete the CNAME record, the subdomain now points to an unclaimed resource name on a third-party platform.
An attacker can register that resource name and immediately receive traffic from your subdomain.
The Attack in Practice
1. Attacker scans your subdomains
2. Finds: staging.yourcompany.com → your-old-app.herokuapp.com
3. Checks if "your-old-app" is available on Heroku
4. Registers "your-old-app" on Heroku (free tier, takes 2 minutes)
5. Deploys content to the app
6. staging.yourcompany.com now serves attacker content
The attacker now controls a page on your domain. They can:
- Serve phishing content that collects employee/customer credentials
- Set cookies scoped to your parent domain (
.yourcompany.com) — these cookies are sent to all your subdomains - Issue a TLS certificate for your subdomain (Let's Encrypt validates DNS control, which they now have)
- Conduct XSS attacks against users who trust your domain
The TLS certificate point is critical: attackers can get a valid, browser-trusted HTTPS certificate for your subdomain. The padlock icon will show. Users have no way to distinguish this from legitimate content.
Vulnerable Services — Common CNAME Targets
Services where resource names are re-claimable:
| Service | CNAME Pattern | Claimable? |
|---|---|---|
| GitHub Pages | *.github.io | Yes |
| Heroku | *.herokuapp.com | Yes |
| Netlify | *.netlify.app | Yes |
| AWS Elastic Beanstalk | *.elasticbeanstalk.com | Yes |
| Azure CDN | *.azureedge.net | Yes |
| Fastly | *.fastly.net | Yes |
| Shopify | *.myshopify.com | Yes |
| Zendesk | *.zendesk.com | Yes |
The canonical resource for testing: can-i-take-over-xyz (GitHub repository listing hundreds of vulnerable services with detection fingerprints).
Detection: What to Look For
A subdomain is vulnerable to takeover when:
- A CNAME record exists pointing to a third-party service
- The target resource at that service returns a "resource not found" or "no such app" response
- The resource name at the third-party service is available for registration
The key detection signal is the third-party error page fingerprint. Each service returns a distinctive response when pointing to an unclaimed resource:
GitHub Pages: "There isn't a GitHub Pages site here."
Heroku: "No such app"
Netlify: "Not Found - Request ID: ..."
Fastly: "Fastly error: unknown domain ..."
AWS/EB: NXDOMAIN or 404 from AWS
An EASM tool that crawls your subdomains and checks for these fingerprints will surface takeover candidates immediately.
The Scale of the Problem
Organizations consistently underestimate how many stale CNAME records they have. Factors that create dangling CNAMEs:
- Team members who provisioned and deprovisioned services without updating DNS
- Automated infrastructure-as-code that deletes cloud resources but doesn't manage DNS
- Acquisitions where the acquired company's DNS was never fully audited
- Development environments that were "temporary" and became permanent
- Platform migrations where the old CNAME was never removed
For a company of 200+ engineers that's been shipping for 3+ years, 10–30 vulnerable subdomains is typical. One actively exploited takeover is a brand crisis.
Remediation
For each confirmed takeover candidate:
Step 1: Delete the CNAME record immediately. DNS propagation takes up to 48 hours; do not wait for it.
Step 2: Verify the target resource is truly deprovisioned. If the CNAME is dangling but the resource still exists (perhaps renamed), understand why before deleting.
Step 3: Add the subdomain to your EASM monitoring scope with an alert on re-appearance.
Preventive measure: Implement a process where DNS record deletion is part of service deprovisioning checklists. Infrastructure-as-code (Terraform, Pulumi) that manages both cloud resources and DNS simultaneously eliminates the manual gap.
Prevention in IaC
If you use Terraform, colocate your DNS and cloud resources in the same module:
resource "heroku_app" "staging" {
name = "yourcompany-staging"
}
resource "cloudflare_record" "staging" {
zone_id = var.zone_id
name = "staging"
value = heroku_app.staging.web_url
type = "CNAME"
# Deleting the heroku_app will also delete this record
# because they're managed together
}
When terraform destroy runs on the Heroku app, it also removes the DNS record. The dangling state never exists.
Continuous Monitoring Is the Only Scalable Defense
Manual DNS audits catch what you find at the moment you look. EASM continuous monitoring catches:
- New CNAMEs added today that point to an already-deprovisioned resource
- Existing CNAMEs where the target resource was just deleted
- Third-party service migrations that invalidated old CNAME targets
PentestCheck monitors your full subdomain inventory daily and alerts on any new dangling CNAME with a service fingerprint match. Takeover candidates surface within hours of becoming vulnerable — before an attacker finds them first.
PentestCheck EASM Engine continuously monitors your DNS records and fingerprints third-party service responses. Takeover candidates are flagged as HIGH severity with remediation instructions.