← Back to writing
Web Pentesting

Subdomain Takeover

Jun 30, 2024
3 min read
lawbyte

Subdomain takeover occurs when a subdomain’s DNS record points to an external service that is no longer claimed by the organization. An attacker claims the unclaimed resource and controls what runs on subdomain.target.com.

How It Happens

  1. Organization sets blog.target.com CNAME blog-target.herokuapp.com
  2. They migrate away from Heroku, delete the app
  3. The DNS record is never removed
  4. blog-target.herokuapp.com is now unclaimed
  5. Attacker creates a Heroku app named blog-target and controls blog.target.com

Impact

  • Full subdomain control — serve any content at blog.target.com
  • Cookie theft — if cookies are scoped to *.target.com
  • XSS — JavaScript on blog.target.com can access cookies shared with the main domain
  • Phishing — trusted domain for credential harvesting
  • OAuth redirect URI — if blog.target.com is a whitelisted redirect, use the takeover for OAuth theft

Enumeration

Step 1 — Find all subdomains

# Passive sources
subfinder -d target.com -all -o subdomains.txt
amass enum -passive -d target.com -o subdomains.txt
assetfinder --subs-only target.com >> subdomains.txt
findomain -t target.com -u subdomains.txt

# Certificate transparency
curl "https://crt.sh/?q=%.target.com&output=json" | jq '.[].name_value' | sort -u

# GitHub dorking
# site:github.com "target.com"

Step 2 — Resolve all subdomains

# dnsx for fast resolution
cat subdomains.txt | dnsx -a -cname -o resolved.txt

# Check for CNAMEs specifically
cat subdomains.txt | dnsx -cname -o cnames.txt

Step 3 — Find dangling CNAMEs

# For each CNAME, check if the target is claimed
while read -r sub cname; do
if ! host $cname &>/dev/null; then
echo "[DANGLING] $sub$cname"
fi
done < cnames.txt

# Or use subjack / subzy for automated detection
subjack -w subdomains.txt -t 100 -timeout 30 -o results.txt -ssl
subzy run --targets subdomains.txt

Service-Specific Fingerprints and Takeover Steps

GitHub Pages

Fingerprint: There isn't a GitHub Pages site here.

# Check if CNAME points to *.github.io
dig blog.target.com CNAME
# → blog-target.github.io

# Verify it's unclaimed
curl -s https://blog.target.com | grep "There isn't a GitHub Pages site"

# Claim:
# 1. Create a GitHub repo named "blog-target"
# 2. Enable GitHub Pages
# 3. Add CNAME file containing "blog.target.com"

AWS S3

Fingerprint: NoSuchBucket or The specified bucket does not exist

dig assets.target.com CNAME
# → assets-target.s3.amazonaws.com

# Verify unclaimed
curl -s https://assets.target.com | grep "NoSuchBucket"

# Claim:
aws s3 mb s3://assets-target --region us-east-1
# Upload content
aws s3 cp index.html s3://assets-target/ --acl public-read

Heroku

Fingerprint: No such app or herokucdn.com

# Claim a Heroku app with matching name
heroku create blog-target
git push heroku main
heroku domains:add blog.target.com

Azure

Fingerprint: 404 Web Site not found

# Azure App Service, Blob Storage, CDN, Traffic Manager
# Each has different claim process through Azure Portal

Fastly

Fingerprint: Fastly error: unknown domain

fastly service create
fastly domain create --name blog.target.com

Shopify

Fingerprint: Sorry, this shop is currently unavailable.

Tumblr

Fingerprint: There's nothing here.

Ghost

Fingerprint: The thing you were looking for is no longer here


Vulnerable Service Reference

Service CNAME Pattern Fingerprint
GitHub Pages *.github.io “There isn’t a GitHub Pages site here”
S3 *.s3.amazonaws.com “NoSuchBucket”
Heroku *.herokuapp.com “No such app”
Azure Websites *.azurewebsites.net “404 Web Site not found”
Fastly *.fastly.net “Fastly error: unknown domain”
Shopify *.myshopify.com “Sorry, this shop is currently unavailable”
Tumblr *.tumblr.com “There’s nothing here”
WP Engine *.wpengine.com “The site you were looking for”
Ghost *.ghost.io “The thing you were looking for is no longer here”
Surge.sh *.surge.sh “project not found”
Netlify *.netlify.app “Not found”

NS-Based Subdomain Takeover

If the NS records for a subdomain zone point to nameservers that can be registered:

dig internal.target.com NS
# → ns1.abandoned-provider.com

# If ns1.abandoned-provider.com domain is unregistered, register it
# and control DNS for internal.target.com

Tools

# subjack — multi-service detector
subjack -w subdomains.txt -t 100 -timeout 30 -ssl -v

# nuclei — subdomain takeover templates
nuclei -l subdomains.txt -t nuclei-templates/takeovers/

# subzy — fast and simple
subzy run --targets subdomains.txt --hide-fails

# can-i-take-over-xyz (GitHub reference)
# Lists all services and their fingerprints

Responsible Disclosure

When you find a subdomain takeover:

  1. Claim the resource to prevent someone else from doing so.
  2. Host a benign page proving control (no malicious content).
  3. Report to the organization with proof.
  4. Wait for them to fix the DNS record before releasing your claim.

Remediation

  • Remove DNS records immediately when deprovisioning cloud resources.
  • Regularly audit DNS records against active cloud resources.
  • Use a DNS inventory tool to track all CNAMEs and their status.
  • Never use externally-controlled naming patterns that could be registered by others.

Discussion

Leave a comment · All fields required · No spam

No comments yet. Be the first.