Subdomain Discovery
Subdomains often host development environments, staging servers, admin panels, and legacy applications with weaker security controls. Comprehensive subdomain enumeration is critical for mapping the complete attack surface.
Why Subdomain Discovery Matters
Subdomains represent hidden attack surface. Development and
staging environments often have default credentials, debug endpoints, and unpatched vulnerabilities.
Finding forgotten subdomains like dev., staging., or old.
frequently leads to critical findings.
Tip
Tools & Resources
Passive Subdomain Discovery
Passive techniques query third-party databases and services without touching target infrastructure.
Subfinder
# Basic subdomain enumeration
subfinder -d example.com -o subdomains.txt
# Silent mode (just results)
subfinder -d example.com -silent
# Multiple domains
subfinder -dL domains.txt -o all_subdomains.txt
# Recursive enumeration
subfinder -d example.com -recursive
# Use all sources (requires API keys configured)
subfinder -d example.com -all
# Output in JSON
subfinder -d example.com -json -o subdomains.json
# Configure sources in ~/.config/subfinder/provider-config.yaml
# Add API keys for: SecurityTrails, Shodan, Censys, VirusTotal, etc.Amass (Passive Mode)
# Passive enumeration only
amass enum -passive -d example.com -o amass_passive.txt
# With specific data sources
amass enum -passive -d example.com -src
# Intel mode for additional discovery
amass intel -d example.com -whois
# Find ASN and expand
amass intel -org "Example Company"
amass intel -asn 12345
# Config file for API keys
amass enum -passive -d example.com -config ~/.config/amass/config.iniCertificate Transparency
# crt.sh - Query CT logs
curl -s "https://crt.sh/?q=%25.example.com&output=json" | \
jq -r '.[].name_value' | sed 's/\*\.//g' | sort -u
# Filter unique subdomains
curl -s "https://crt.sh/?q=%.example.com&output=json" | \
jq -r '.[].name_value' | \
grep -v "\*" | \
sort -u > ct_subdomains.txt
# Certspotter API
curl -s "https://api.certspotter.com/v1/issuances?domain=example.com&include_subdomains=true&expand=dns_names" | \
jq -r '.[].dns_names[]' | sort -u
# Facebook CT
curl -s "https://graph.facebook.com/certificates?query=example.com&access_token=TOKEN" | jqOther Passive Sources
# Assetfinder
assetfinder --subs-only example.com
# Findomain
findomain -t example.com -q
# Chaos (ProjectDiscovery database)
chaos -d example.com -o chaos_subs.txt
# Archive-based discovery
# Wayback URLs
waybackurls example.com | unfurl --unique domains
# GAU (GetAllUrls)
gau --subs example.com | unfurl --unique domains
# SecurityTrails API
curl -s "https://api.securitytrails.com/v1/domain/example.com/subdomains" \
-H "APIKEY: your_api_key" | jq -r '.subdomains[]' | \
sed 's/$/.example.com/'
# VirusTotal API
curl -s "https://www.virustotal.com/vtapi/v2/domain/report?apikey=KEY&domain=example.com" | \
jq -r '.subdomains[]'Active Subdomain Discovery
Warning
DNS Brute-Forcing
# Amass active mode with brute-force
amass enum -active -d example.com -brute -w /path/to/wordlist.txt
# DNSRecon brute-force
dnsrecon -d example.com -t brt -D /path/to/wordlist.txt
# Knockpy
knockpy example.com -w /path/to/wordlist.txt
# Gobuster DNS mode
gobuster dns -d example.com -w /path/to/wordlist.txt -t 50
# Aiodnsbrute (async, fast)
aiodnsbrute -w /path/to/wordlist.txt example.com
# Massdns (extremely fast, use with wordlist)
massdns -r resolvers.txt -t A -o S wordlist.txt | grep example.comRecommended Wordlists
| Wordlist | Size | Use Case |
|---|---|---|
| SecLists DNS | ~100k entries | General purpose, good balance |
| Jhaddix all.txt | ~2M entries | Comprehensive, slow but thorough |
| n0kovo_subdomains | ~3M entries | Large-scale enumeration |
| commonspeak2 | ~4k entries | Quick initial scan |
# Download common wordlists
# SecLists
git clone https://github.com/danielmiessler/SecLists.git
# Best subdomain list: SecLists/Discovery/DNS/subdomains-top1million-110000.txt
# Jhaddix all.txt
wget https://gist.githubusercontent.com/jhaddix/86a06c5dc309d08580a018c66354a056/raw/all.txt
# Assetnote Wordlists
wget https://wordlists-cdn.assetnote.io/data/manual/best-dns-wordlist.txt
# Generate custom wordlist from target
# Scrape website and extract words
cewl https://example.com -d 3 -w custom_wordlist.txtSubdomain Validation
After enumeration, validate which subdomains are live and gather additional information.
# httpx - Fast HTTP probing
cat subdomains.txt | httpx -silent -o live_subdomains.txt
# With additional data
cat subdomains.txt | httpx -title -status-code -tech-detect -o httpx_results.txt
# JSON output with all details
cat subdomains.txt | httpx -json -o httpx_results.json
# Filter by status code
cat subdomains.txt | httpx -mc 200,301,302,403
# Screenshot live hosts
cat subdomains.txt | httpx -screenshot -ss-timeout 5
# Check for specific technologies
cat subdomains.txt | httpx -tech-detect | grep -i "wordpress\|joomla\|drupal"# DNS resolution check
cat subdomains.txt | dnsx -silent -a -resp
# Check CNAME for subdomain takeover
cat subdomains.txt | dnsx -silent -cname -resp
# Mass DNS resolution with massdns
massdns -r resolvers.txt -t A -o S subdomains.txt > resolved.txt
# Filter only resolved
cat resolved.txt | grep -E "\. A " | cut -d " " -f 1 | sed 's/\.$//' > resolved_subs.txtSubdomain Takeover Detection
Subdomains pointing to unclaimed cloud services or expired resources can be taken over.
# Check CNAME records for takeover candidates
cat subdomains.txt | dnsx -cname -resp | grep -E "(amazonaws|azure|cloudfront|herokuapp|github|shopify|fastly|pantheon|zendesk|ghost)"
# nuclei subdomain takeover templates
nuclei -l subdomains.txt -t ~/nuclei-templates/takeovers/
# Subjack - Subdomain takeover checker
subjack -w subdomains.txt -t 100 -timeout 30 -ssl -c fingerprints.json -v
# Can-I-Take-Over-XYZ reference
# https://github.com/EdOverflow/can-i-take-over-xyz
# Common takeover fingerprints:
# - "There isn't a GitHub Pages site here" (GitHub)
# - "NoSuchBucket" (AWS S3)
# - "The specified bucket does not exist" (GCS)
# - "Sorry, this shop is currently unavailable" (Shopify)
# - "Fastly error: unknown domain" (Fastly)Automation Pipeline
#!/bin/bash
# Comprehensive subdomain enumeration pipeline
DOMAIN=$1
OUTPUT_DIR="recon/$DOMAIN"
mkdir -p $OUTPUT_DIR
echo "[*] Running subdomain enumeration for $DOMAIN"
# Passive sources
echo "[+] Subfinder..."
subfinder -d $DOMAIN -silent >> $OUTPUT_DIR/subs_raw.txt
echo "[+] Amass passive..."
amass enum -passive -d $DOMAIN -silent >> $OUTPUT_DIR/subs_raw.txt
echo "[+] Assetfinder..."
assetfinder --subs-only $DOMAIN >> $OUTPUT_DIR/subs_raw.txt
echo "[+] crt.sh..."
curl -s "https://crt.sh/?q=%25.$DOMAIN&output=json" | \
jq -r '.[].name_value' 2>/dev/null | sed 's/\*\.//g' >> $OUTPUT_DIR/subs_raw.txt
# Deduplicate
echo "[+] Deduplicating..."
cat $OUTPUT_DIR/subs_raw.txt | sort -u > $OUTPUT_DIR/subdomains.txt
# Resolve and probe
echo "[+] Probing live hosts..."
cat $OUTPUT_DIR/subdomains.txt | httpx -silent -threads 100 -o $OUTPUT_DIR/live_subdomains.txt
# Results
TOTAL=$(wc -l < $OUTPUT_DIR/subdomains.txt)
LIVE=$(wc -l < $OUTPUT_DIR/live_subdomains.txt)
echo "[*] Found $TOTAL subdomains, $LIVE are live"
# Check for takeover
echo "[+] Checking subdomain takeover..."
nuclei -l $OUTPUT_DIR/subdomains.txt -t ~/nuclei-templates/takeovers/ -silent -o $OUTPUT_DIR/takeovers.txt
echo "[*] Results saved to $OUTPUT_DIR"Subdomain Discovery Checklist
🔍 Passive Discovery
- ☐ Subfinder enumeration
- ☐ Amass passive mode
- ☐ Certificate Transparency logs
- ☐ Wayback/Archive URLs
- ☐ SecurityTrails/VirusTotal APIs
⚡ Active Discovery
- ☐ DNS brute-forcing
- ☐ Multiple wordlists tested
- ☐ Permutation generation
- ☐ Zone transfer attempted
- ☐ Virtual host discovery
✅ Validation
- ☐ Live hosts identified (httpx)
- ☐ Technologies detected
- ☐ Status codes documented
- ☐ Screenshots captured
- ☐ Results deduplicated
🎯 Takeover Check
- ☐ CNAME records analyzed
- ☐ Cloud services identified
- ☐ Takeover fingerprints checked
- ☐ Nuclei takeover templates
- ☐ Dangling records documented