Subdomain Fuzzing (DNS)¶
Why Subdomain Fuzzing?¶
Organizations often expose more attack surface through subdomains than through directory paths. A company might have mail.target.com, dev.target.com, staging.target.com, api.target.com, jenkins.target.com — each potentially running different applications with different security postures.
Subdomain fuzzing brute-forces DNS records to discover these hidden assets. Unlike directory fuzzing (which tests URL paths against one server), subdomain fuzzing queries the DNS resolver for each candidate hostname.
How It Works¶
When you fuzz subdomains, ffuf sends an HTTP request to each candidate subdomain. If the subdomain has a valid DNS record that resolves to an IP, you get a response. If it doesn't resolve, the connection fails.
graph LR
A[ffuf] -->|"dev.target.com"| B[DNS Resolver]
B -->|"10.10.10.5"| C[Web Server]
C -->|"200 OK"| A
A -->|"fake.target.com"| B
B -->|"NXDOMAIN"| A
Basic Subdomain Fuzzing Command¶
ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ \
-u http://FUZZ.target.com
This replaces FUZZ with each line in the wordlist, making requests to:
http://www.target.comhttp://mail.target.comhttp://dev.target.comhttp://api.target.com- ...
Choosing the Right Wordlist¶
| Wordlist | Entries | Use Case |
|---|---|---|
subdomains-top1million-5000.txt |
4,989 | Quick first pass |
subdomains-top1million-20000.txt |
19,966 | Standard engagement |
subdomains-top1million-110000.txt |
114,441 | Thorough scan |
dns-Jhaddix.txt |
~2.2M | Bug bounty (slow but comprehensive) |
bitquark-subdomains-top100000.txt |
100,000 | Good alternative |
All located under /usr/share/seclists/Discovery/DNS/.
Start Small
The top-5000 list catches the vast majority of common subdomain names. Only escalate to larger lists if you suspect the target has non-standard naming (internal tools, project codenames).
Practical Walkthrough¶
Step 1: Run the Initial Scan¶
ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ \
-u http://FUZZ.academy.htb
________________________________________________
:: Method : GET
:: URL : http://FUZZ.academy.htb
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
:: Threads : 40
________________________________________________
www [Status: 200, Size: 986, Words: 423, Lines: 32, Duration: 44ms]
test [Status: 200, Size: 986, Words: 423, Lines: 32, Duration: 43ms]
admin [Status: 200, Size: 986, Words: 423, Lines: 32, Duration: 45ms]
mail [Status: 200, Size: 986, Words: 423, Lines: 32, Duration: 42ms]
portal [Status: 200, Size: 986, Words: 423, Lines: 32, Duration: 44ms]
dev [Status: 200, Size: 10458, Words: 2145, Lines: 189, Duration: 48ms]
Step 2: Identify the Problem¶
Notice that most results have the same size (986 bytes). This is a wildcard DNS record — the server responds to ANY subdomain with a default page. Only dev has a different size (10458), meaning it serves unique content.
Step 3: Filter the Noise¶
ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ \
-u http://FUZZ.academy.htb -fs 986
________________________________________________
dev [Status: 200, Size: 10458, Words: 2145, Lines: 189, Duration: 48ms]
Now you have a real result: dev.academy.htb.
Dealing with Wildcard DNS¶
Many domains use wildcard DNS records (*.target.com → IP) which makes every subdomain "resolve." The key is to filter by response size, word count, or line count since the wildcard page has a consistent fingerprint.
# Method 1: Check the wildcard response
curl -s http://thisdoesnotexist123.target.com | wc -c
# Output: 986
# Method 2: Use auto-calibration
ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ \
-u http://FUZZ.target.com -ac
Auto-Calibration Isn't Always Perfect
-ac works well when the wildcard response is static. If the default page includes dynamic content (timestamps, random tokens), the size may vary slightly. In that case, use -fw (word count) which is usually more stable than byte size.
Verification with DNS Tools¶
Once you find subdomains, verify them with DNS tools:
# Verify DNS resolution
dig dev.academy.htb
# Quick nslookup
nslookup dev.academy.htb
# Check what IP it resolves to
host dev.academy.htb
This confirms: - The subdomain actually resolves (not just a webserver catchall) - What IP address it points to (same server or different?) - Whether CNAME/A records exist
Lab Environment Setup¶
In exam/lab scenarios, targets often don't have real DNS. You need to add them to /etc/hosts:
# Add discovered subdomains to /etc/hosts
echo "10.10.10.5 academy.htb dev.academy.htb admin.academy.htb" | sudo tee -a /etc/hosts
/etc/hosts Format
Each line maps an IP to one or more hostnames. You can put multiple hostnames on one line:
Workflow for Lab Environments¶
# 1. Add the base domain
echo "10.10.10.5 academy.htb" | sudo tee -a /etc/hosts
# 2. Fuzz for subdomains
ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ \
-u http://FUZZ.academy.htb -fs 986
# 3. Add discovered subdomains
echo "10.10.10.5 dev.academy.htb" | sudo tee -a /etc/hosts
# 4. Browse to the discovered subdomain
curl http://dev.academy.htb
DNS vs. VHost Fuzzing¶
| Aspect | Subdomain (DNS) Fuzzing | VHost Fuzzing |
|---|---|---|
| Mechanism | Changes the hostname in the URL | Changes the Host header |
| Requires DNS | Yes (or /etc/hosts) |
No — works against an IP directly |
| Discovers | Subdomains with DNS records | Virtual hosts on the same IP |
| Catches | Public/resolvable subdomains | Internal/non-DNS-registered vhosts |
| When to use | First — for public targets | After — for targets behind a single IP |
Use Both
Subdomain fuzzing finds what's publicly resolvable. VHost fuzzing (next page) finds what the web server is configured to serve but doesn't have a DNS entry. Always do both.
Combining with Other Subdomain Tools¶
Ffuf is fast but DNS-only. Combine with other tools for comprehensive coverage:
# Passive enumeration (no direct contact)
subfinder -d target.com -o subdomains.txt
# Certificate transparency logs
curl -s "https://crt.sh/?q=%.target.com&output=json" | jq -r '.[].name_value' | sort -u
# DNS brute-force (alternative to ffuf)
gobuster dns -d target.com -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
# Merge results and deduplicate
cat subdomains-*.txt | sort -u > all-subdomains.txt
Revision Recap
- Subdomain fuzzing discovers hidden assets by testing DNS-resolvable hostnames
- Basic pattern:
ffuf -w dns-wordlist:FUZZ -u http://FUZZ.target.com - Wildcard DNS is the main challenge — filter with
-fsor-ac - Start with
subdomains-top1million-5000.txt(fast, catches most common names) - Verify findings with
dig/nslookup/host - In lab environments, add discovered subdomains to
/etc/hosts - Subdomain fuzzing only finds DNS-resolvable hosts — use VHost fuzzing to catch the rest
➡️ Next: VHost Fuzzing — discover virtual hosts without DNS entries