← Back to writing
Tools & Cheatsheets

Subdomain Enumeration

Feb 25, 2025
3 min read
lawbyte

Subdomain enumeration expands your attack surface. Dev servers, staging environments, forgotten admin panels, and legacy applications all live on subdomains.

Passive Enumeration

subfinder

# Install
go install -v github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest

# Basic
subfinder -d target.com

# All sources
subfinder -d target.com -all

# Multiple domains
subfinder -dL domains.txt

# JSON output
subfinder -d target.com -o subdomains.txt -oJ

# Verbose with sources
subfinder -d target.com -v

# Set API keys (~/.config/subfinder/provider-config.yaml)
# Shodan, Censys, SecurityTrails, VirusTotal, etc.

amass

# Install
go install -v github.com/owasp-amass/amass/v4/...@master

# Passive only (no DNS resolution)
amass enum -passive -d target.com

# Active with brute
amass enum -active -d target.com -brute

# With wordlist
amass enum -d target.com -brute -w /usr/share/wordlists/dnsmap.txt

# Multiple domains
amass enum -passive -df domains.txt

# Database tracking
amass db -list
amass db -show -d target.com

# Visualize
amass viz -d3 -d target.com -o viz.html

Certificate Transparency

# crt.sh
curl -s "https://crt.sh/?q=%.target.com&output=json" | \
jq -r '.[].name_value' | \
sed 's/\*\.//g' | \
sort -u

# chaos (ProjectDiscovery's passive dataset)
chaos -d target.com -o chaos.txt

# SecurityTrails API
curl -H "APIKEY: YOUR_KEY" \
"https://api.securitytrails.com/v1/domain/target.com/subdomains" | \
jq '.subdomains[]' | sed 's/"//g' | sed 's/$/.target.com/'

# VirusTotal
curl "https://www.virustotal.com/vtapi/v2/domain/report?apikey=KEY&domain=target.com" | \
jq '.subdomains[]'

# AlienVault OTX
curl "https://otx.alienvault.com/api/v1/indicators/domain/target.com/passive_dns" | \
jq '.passive_dns[].hostname'

Active Enumeration (DNS Brute Force)

puredns

# Install
go install github.com/d3mondev/puredns/v2@latest

# Brute force with wordlist
puredns bruteforce wordlist.txt target.com

# With resolvers
puredns bruteforce wordlist.txt target.com -r resolvers.txt

# Resolve existing list
puredns resolve subdomains.txt -r resolvers.txt

# Download good resolvers
curl -s https://raw.githubusercontent.com/trickest/resolvers/main/resolvers.txt -o resolvers.txt

massdns + shuffledns

# shuffledns (wrapper around massdns)
go install -v github.com/projectdiscovery/shuffledns/cmd/shuffledns@latest

# Brute force
shuffledns -d target.com -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt \
-r resolvers.txt

# Resolve existing list
shuffledns -d target.com -list subdomains.txt -r resolvers.txt

# massdns directly
massdns -r resolvers.txt -t A -o S subdomains_with_domain.txt

dnsx

# Install
go install -v github.com/projectdiscovery/dnsx/cmd/dnsx@latest

# Resolve subdomains
cat subdomains.txt | dnsx -resp

# Get A records
cat subdomains.txt | dnsx -a -resp

# Get CNAME (useful for takeover detection)
cat subdomains.txt | dnsx -cname -resp

# All record types
cat subdomains.txt | dnsx -a -aaaa -cname -ns -txt -mx -resp

# Brute with wordlist
dnsx -d target.com -w wordlist.txt -resp

Virtual Host Discovery

When multiple sites share one IP, vhosts reveal hidden apps:

# ffuf
ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
-u https://TARGET_IP/ \
-H "Host: FUZZ.target.com" \
-mc 200,301,302,403 \
-fs FILTER_SIZE

# gobuster vhost
gobuster vhost -u http://TARGET_IP -w wordlist.txt --append-domain

# With HTTPS
gobuster vhost -u https://TARGET_IP -w wordlist.txt --append-domain -k

# wfuzz
wfuzz -c -w wordlist.txt -u https://TARGET_IP/ -H "Host: FUZZ.target.com" \
--hc 404 --hw WORDS

Filtering Live Hosts

# httpx (ProjectDiscovery)
go install -v github.com/projectdiscovery/httpx/cmd/httpx@latest

# Check live HTTP hosts
cat subdomains.txt | httpx -silent

# With status codes and titles
cat subdomains.txt | httpx -status-code -title -tech-detect

# With response size (to identify wildcards)
cat subdomains.txt | httpx -status-code -content-length -silent

# Save alive hosts
cat subdomains.txt | httpx -o live.txt

# Screenshot with aquatone
cat subdomains.txt | aquatone -out screenshots/

# With gowitness
cat subdomains.txt | httpx -silent | gowitness file --file -
gowitness file -f live.txt --screenshot-path ./screenshots

Wordlists

# Recommended wordlists
# Seclists (best all-round)
/usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
/usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
/usr/share/seclists/Discovery/DNS/dns-Jhaddix.txt

# Best DNS brute wordlist
wget https://wordlists-cdn.assetnote.io/data/manual/best-dns-wordlist.txt

# Fresh from Commonspeak2 (scraped from GitHub/Common Crawl)
wget https://wordlists-cdn.assetnote.io/data/automated/httparchive_subdomains_2024_11_01.txt

# Install SecLists
sudo apt install seclists
# or
git clone https://github.com/danielmiessler/SecLists.git /usr/share/seclists/

Full Recon Pipeline

#!/bin/bash
DOMAIN=$1
OUTPUT="recon_$DOMAIN"
mkdir -p $OUTPUT

echo "[*] Passive recon..."
subfinder -d $DOMAIN -all -o $OUTPUT/subfinder.txt
amass enum -passive -d $DOMAIN -o $OUTPUT/amass.txt
curl -s "https://crt.sh/?q=%.${DOMAIN}&output=json" | jq -r '.[].name_value' | \
sort -u > $OUTPUT/crtsh.txt

echo "[*] Merging..."
cat $OUTPUT/subfinder.txt $OUTPUT/amass.txt $OUTPUT/crtsh.txt | \
sed 's/\*\.//g' | sort -u > $OUTPUT/all_subs.txt
echo "[+] Total: $(wc -l < $OUTPUT/all_subs.txt) subdomains"

echo "[*] DNS resolution..."
puredns resolve $OUTPUT/all_subs.txt -r resolvers.txt -w $OUTPUT/resolved.txt

echo "[*] HTTP probing..."
cat $OUTPUT/resolved.txt | httpx -silent -status-code -title -o $OUTPUT/live.txt

echo "[*] Screenshots..."
gowitness file -f $OUTPUT/live.txt --screenshot-path $OUTPUT/screenshots/

echo "[+] Done! Results in $OUTPUT/"

Wildcard Detection

Some domains return results for any subdomain (wildcard DNS):

# Test for wildcard
dig randomstring12345.target.com
# If it resolves → wildcard DNS

# puredns handles wildcards automatically
# dnsx wildcard filter
cat subdomains.txt | dnsx -resp -wc -wd target.com

# Manual detection: if ALL subdomains resolve to the same IP,
# it's likely a wildcard. Filter by response size in httpx.

Subdomain Takeover Scanning

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

# subjack
subjack -w subdomains.txt -t 100 -timeout 30 -o takeovers.txt -ssl

# subzy
subzy run --targets subdomains.txt

# dnsx CNAME check + manual verification
cat subdomains.txt | dnsx -cname -resp | grep CNAME
# Check if CNAME target is claimable (returns NXDOMAIN)

Discussion

Leave a comment · All fields required · No spam

No comments yet. Be the first.