June 14, 2026
Subdomain Enumeration: A Core Technique Every Bug Hunter Must Master
Deep Dive with Real-World Examples, Case Studies, and Battle-Tested Workflows
MD Mehedi Hasan
21 min read
Table of Contents
- Why Subdomain Enumeration Defines Your Success Rate
- The Two Pillars: Passive vs. Active Enumeration
- Real-World Case Study #1: The ATG Swedish Betting Brand Walkthrough
- Real-World Case Study #2: $5,000 USDC — Subdomain Takeover on a Blockchain Project
- Real-World Case Study #3: The GoHire Takeover — A HackerOne Classic
- Real-World Case Study #4: The Freshdesk Zero-Day — Thousands of Subdomains Vulnerable
- Real-World Case Study #5: Subdomain Discovery Exposing an .env File with GitHub Tokens
- The Complete Toolchain in 2026
- Phase 1: Comprehensive Passive Enumeration — Hands-On
- Phase 2: Active Brute-Force — From Theory to Shell
- Phase 3: Permutation and Mutation Attacks
- Phase 4: ASN-Based and Infrastructure-Aware Enumeration
- Phase 5: Virtual Host Fuzzing
- Phase 6: Subdomain Takeover Detection and Exploitation
- What Happens After Takeover? CORS, Cookies, OAuth, and CSP Chaining
- The Production-Grade Automated Pipeline
- Lessons from the Trenches — What the Best Hunters Do Differently
- Final Words
1. Why Subdomain Enumeration Defines Your Success Rate
Every bug bounty hunter has experienced this: you spend hours probing www.target.com, find nothing, and walk away empty-handed. Meanwhile, another hunter discovered staging-api.target.com, found an exposed .env file, and cashed a $5,000 check.
Subdomain enumeration is the single highest-leverage activity in reconnaissance. It is not a checkbox item. It is the foundation upon which every successful hunt is built.
Every subdomain represents:
- An expanded attack surface — more endpoints to probe
- A forgotten asset — staging servers, internal dashboards, legacy apps
- A cloud service with dangling DNS — subdomain takeover waiting to happen
- A vulnerable configuration — CORS misconfigurations, cookie scoping issues, CSP bypasses
Consider this: during the Panama Papers investigation, journalists and security researchers mapped Mossack Fonseca's attack surface through subdomain enumeration, uncovering exposed internal systems and email portals that became central to the entire story. Subdomain enumeration didn't just find bugs — it uncovered one of the largest data leaks in history.
In bug bounty, the math is simple: more subdomains = more attack surface = more vulnerabilities = more bounty dollars.
Let's walk through real-world examples that prove this point.
2. The Two Pillars: Passive vs. Active Enumeration
Before we dive into specific tools and commands, you must understand the fundamental distinction that governs every subdomain discovery technique.
Passive Enumeration — The Silent Informant
Passive enumeration gathers subdomain data without sending a single packet to the target's infrastructure. You query third-party sources — certificate transparency logs, search engines, DNS aggregators, WHOIS databases, web archives — that have already recorded information about the target.
Why it matters: The target never sees you coming. No logs, no alerts, no WAF triggers.
Primary sources:
- Certificate Transparency (CT) logs — crt.sh, CertSpotter, Google CT
- Search engines — Google
site:operator, Bing, DuckDuckGo - DNS aggregators — SecurityTrails, AlienVault OTX, VirusTotal
- Web archives — Wayback Machine, Common Crawl
- WHOIS and reverse WHOIS
- GitHub and code repositories — leaked configurations, hardcoded URLs
Active Enumeration — The Direct Approach
Active enumeration directly interacts with the target's DNS infrastructure. You send DNS queries, brute-force wordlists, crawl web applications, or perform virtual host fuzzing.
Why it matters: It finds subdomains that no public source has ever indexed — the deep, forgotten, internal-facing assets.
Primary techniques:
- DNS brute-forcing with wordlists
- DNS zone transfer (AXFR) attempts
- NSEC walking on DNSSEC-enabled zones
- Virtual host enumeration via HTTP Host header fuzzing
- Reverse DNS lookups on owned IP ranges
- Web crawling and link extraction
The golden rule: passive-first, active-second. Exhaust every passive source before you touch the target's DNS. You'll find 60–80% of all discoverable subdomains without ever making a sound.
3. Real-World Case Study #1: The ATG Swedish Betting Brand Walkthrough
Source: YesWeHack Public Bug Bounty Program — ATG (Svenska Spel)
The Swedish betting brand ATG runs a public bug bounty program on YesWeHack. Security researchers have used ATG as a live demonstration target to illustrate how subdomain enumeration uncovers the hidden attack surface of a real, production organization.
The Passive Reconnaissance Phase
Researchers started with passive enumeration using tools that query third-party databases without touching ATG's infrastructure:
# Step 1: Subfinder — queries 30+ passive sources
subfinder -d atg.se -all -o subfinder_atg.txt
# Step 2: crt.sh — certificate transparency logs
curl -s "https://crt.sh/?q=%25.atg.se&output=json" | \
jq -r '.[].name_value' | sed 's/\*\.//g' | sort -u > crtsh_atg.txt
# Step 3: Wayback Machine — historical subdomains
curl -s "http://web.archive.org/cdx/search/cdx?url=*.atg.se/*&output=text&fl=original&collapse=urlkey" | \
sed 's|https\?://\([^/]*\).*|\1|' | sort -u > wayback_atg.txt# Step 1: Subfinder — queries 30+ passive sources
subfinder -d atg.se -all -o subfinder_atg.txt
# Step 2: crt.sh — certificate transparency logs
curl -s "https://crt.sh/?q=%25.atg.se&output=json" | \
jq -r '.[].name_value' | sed 's/\*\.//g' | sort -u > crtsh_atg.txt
# Step 3: Wayback Machine — historical subdomains
curl -s "http://web.archive.org/cdx/search/cdx?url=*.atg.se/*&output=text&fl=original&collapse=urlkey" | \
sed 's|https\?://\([^/]*\).*|\1|' | sort -u > wayback_atg.txtWhat they found: Multiple subdomains that weren't linked from any public page — including internal API endpoints, staging environments, and regional variants of the main betting platform.
The Active Reconnaissance Phase
With the passive results in hand, researchers moved to active brute-forcing:
# Step 4: Gobuster DNS brute-force
gobuster dns -d atg.se -w ~/wordlists/subdomains-top1million-5000.txt -o gobuster_atg.txt
# Step 5: Virtual host fuzzing with ffuf
ffuf -c -r -u 'https://www.atg.se/' -H 'Host: FUZZ.atg.se' -w ~/wordlists/dns-wordlist.txt# Step 4: Gobuster DNS brute-force
gobuster dns -d atg.se -w ~/wordlists/subdomains-top1million-5000.txt -o gobuster_atg.txt
# Step 5: Virtual host fuzzing with ffuf
ffuf -c -r -u 'https://www.atg.se/' -H 'Host: FUZZ.atg.se' -w ~/wordlists/dns-wordlist.txtThe vhost fuzzing result: ffuf returned unusual HTTP responses for several subdomain names that didn't have DNS records — meaning they existed as virtual hosts on the same web server but weren't publicly resolvable. These represented hidden applications sharing the same infrastructure.
The Web Crawling Phase
Researchers then used Burp Suite's embedded browser to navigate to www.atg.se and passively collected subdomains from the page's DOM, API calls, and JavaScript bundles:
"We collected a bunch of subdomains from a single domain visit. It's typical to get such a positive result, because modern websites tend to use multiple API endpoints and microservices, each with their own dedicated subdomain."
The HTTP Probing Phase
All collected subdomains were merged and filtered through httpx to identify live web servers:
cat subfinder_atg.txt crtsh_atg.txt wayback_atg.txt gobuster_atg.txt | sort -u > all_atg_subs.txt
cat all_atg_subs.txt | httpx -silent -title -status-code -tech-detect -o live_atg_hosts.txtcat subfinder_atg.txt crtsh_atg.txt wayback_atg.txt gobuster_atg.txt | sort -u > all_atg_subs.txt
cat all_atg_subs.txt | httpx -silent -title -status-code -tech-detect -o live_atg_hosts.txtKey Takeaway from ATG
ATG's infrastructure — like most modern enterprises — was a sprawl of microservices, APIs, and regional deployments. No single tool caught everything. Subfinder found different assets than crt.sh, which found different assets than the Wayback Machine. The full picture only emerged after layering passive sources, active brute-force, vhost fuzzing, and web crawling.
4. Real-World Case Study #2: $5,000 USDC — Subdomain Takeover on a Blockchain Project
Source: Public bug bounty writeup by Foysal Ahmed (2025)
This is one of the most instructive real-world subdomain takeover stories because it demonstrates the entire chain — from enumeration to exploitation to payout — on a high-value target.
The Discovery
The hunter was not working on a traditional bug bounty platform (HackerOne, Bugcrowd). Instead, he was hunting "in the wild" — scanning the internet for dangling DNS records on blockchain and crypto projects, which are known to have high payouts due to the financial nature of their user base.
Step 1: Subdomain Enumeration
Using the standard toolkit — Subfinder, Amass, crt.sh, and SecurityTrails — the hunter enumerated subdomains for the target blockchain project. One subdomain stood out:
dev4.site.comdev4.site.comThe name dev4 immediately suggested a development/staging environment — precisely the kind of forgotten asset that organizations fail to decommission properly.
Step 2: DNS Verification
dig CNAME dev4.site.comdig CNAME dev4.site.comThe response revealed the subdomain was a CNAME pointing to an Amazon S3 bucket endpoint. This is the classic signature of a subdomain takeover vulnerability — a DNS record pointing to a cloud service that may no longer be provisioned.
Step 3: Confirming the Vulnerability
curl -I http://dev4.site.comcurl -I http://dev4.site.comThe HTTP response contained the telltale error message:
"NoSuchBucket""NoSuchBucket"This is Amazon S3's canonical error when a bucket name doesn't exist. The CNAME record existed, but the S3 bucket it pointed to had been deleted. Anyone who creates an S3 bucket with that exact name can claim the subdomain.
Step 4: The Takeover
The hunter created an AWS account, navigated to S3, and created a bucket with the exact name from the CNAME target. Within minutes, dev4.site.com was serving his content.
Step 5: Proof of Concept
Rather than defacing the page (which is both unethical and against responsible disclosure practices), the hunter:
- Cloned the main website's front-end by copying assets and design elements
- Added a small JavaScript payload that demonstrated:
- Logging visitor IP addresses
- A fake "Connect Wallet" button to simulate phishing (critical for a blockchain audience)
This proved that an attacker could have used the subdomain to phish wallet credentials from unsuspecting users — a devastating attack vector for a crypto platform.
The Payout
The company acknowledged the report and awarded 5,000 USDC (approximately $5,000 USD).
Key Takeaway
This case illustrates the full value chain: passive enumeration → CNAME inspection → cloud service fingerprinting → takeover → impact demonstration → bounty. The hunter didn't need to find a SQL injection or an RCE. He found a forgotten DNS record.
5. Real-World Case Study #3: The GoHire Takeover — A HackerOne Classic
Source: HackerOne's official "Guide to Subdomain Takeovers 2.0"
This is the canonical example used in HackerOne's own documentation, and it perfectly illustrates the subdomain takeover lifecycle.
The Scenario
A bug bounty program for example.com is in scope. During subdomain enumeration, the hunter discovers:
subdomain.example.comsubdomain.example.comDNS Inspection
host subdomain.example.comhost subdomain.example.comOutput:
subdomain.example.com is an alias for custom.gohire.io.subdomain.example.com is an alias for custom.gohire.io.The subdomain is a CNAME pointing to GoHire — a third-party hiring platform that allows customers to set custom domains for their branded job boards.
The Vulnerability
When navigating to subdomain.example.com, the hunter encounters a 404 error page from GoHire:
"The 404 page suggests that no content is being served under the top-level directory."
The company had deleted their GoHire instance but forgot to remove the DNS CNAME record. The DNS still pointed to GoHire, but no GoHire application was configured to serve content on that domain.
The Exploitation
The hunter created their own GoHire account and configured the custom domain to point to subdomain.example.com. GoHire's system verified the DNS CNAME and allowed the configuration.
Result: The hunter now controlled subdomain.example.com and could serve arbitrary content under the trusted example.com domain.
The Impact Chain
HackerOne's guide explains that once you control a subdomain, the attack possibilities multiply:
- Cookie manipulation — You can set cookies scoped to
.example.comfrom the hijacked subdomain, potentially leading to session hijacking on the main domain - CORS bypass — If
example.comtrusts*.example.comin its CORS policy, you can exfiltrate authenticated data - OAuth redirect abuse — If OAuth allows
*.example.comas a redirect URI, you can steal OAuth tokens - CSP bypass — If the CSP includes the hijacked subdomain in
script-src, you can execute arbitrary JavaScript in the main site's context
Key Takeaway
The GoHire case is so instructive because it illustrates a third-party dependency chain vulnurability. The organization didn't write vulnerable code — they simply forgot to clean up a DNS record when retiring a SaaS product. This is the most common root cause of subdomain takeovers.
6. Real-World Case Study #4: The Freshdesk Zero-Day — Thousands of Subdomains Vulnerable
Source: Researcher disclosure (2022) — Zomato, DHL, StackOverflow, and more
This case is extraordinary because it demonstrates how a single zero-day fingerprint can lead to thousands of vulnerable subdomains across the internet's most trafficked websites.
The Discovery
A security researcher found that Freshdesk (a popular customer support SaaS platform) had a vulnerability in its custom domain feature. When a Freshdesk subdomain was deprovisioned but the DNS CNAME remained, an attacker could claim it — even on domains where Freshdesk had not yet patched the behavior.
The Fingerprint
The researcher identified the unique error response that Freshdesk returned for unclaimed custom domains. They created a custom Nuclei template and a Subjack configuration to detect this fingerprint at scale.
The Mass Scan
The researcher didn't just scan their target's scope. They scanned:
- All subdomains of their bug bounty targets
- The Alexa Top 1 Million websites
The Results
Thousands of subdomains were vulnerable, including:
The Payout
The researcher submitted more than 10 reports across different bug bounty programs and earned approximately $5,000 in total rewards.
Key Takeaway
This case illustrates the power of fingerprinting and automation. A single vulnerability class — unclaimed Freshdesk subdomains — yielded thousands of findings across the internet. The researcher didn't need to find 10 unique bugs. They found one pattern and scaled it.
The lesson for hunters: Build custom fingerprints for services not yet in the public databases. That's where the undiscovered bounties live.
7. Real-World Case Study #5: Subdomain Discovery Exposing an .env File with GitHub Tokens
Source: Bug bounty writeup (Medium, 2025)
This case shows that subdomain enumeration doesn't always lead to a takeover — sometimes it leads to something even more valuable.
The Discovery
During passive enumeration of a large tech company's program, a hunter discovered a subdomain that didn't appear in any standard search results. It was found through certificate transparency logs:
dev-dashboard.target.comdev-dashboard.target.comThe Probe
curl -s http://dev-dashboard.target.com/.envcurl -s http://dev-dashboard.target.com/.envThe .env file was completely exposed — no authentication, no IP restriction, no .htaccess protection. Inside the file:
DB_HOST=production-db.internal
DB_USER=admin
DB_PASS=supersecretpassword123
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...
GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxDB_HOST=production-db.internal
DB_USER=admin
DB_PASS=supersecretpassword123
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...
GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxThe Escalation
The GitHub token had access to the organization's private repositories, including source code for production applications. The hunter demonstrated that they could:
- Clone private repositories
- Read source code containing additional API keys
- Access internal documentation with infrastructure diagrams
The Payout
This was reported as a critical severity vulnerability. The bounty amount was not publicly disclosed but was reported to be in the $10,000+ range due to the scope of exposure.
Key Takeaway
Subdomain enumeration found the door. The .env file provided the keys to the kingdom. This is why subdomain enumeration is never just about finding subdomains — it's about finding the forgotten assets where security hasn't been applied.
The hunter noted: "The subdomain dev-dashboard.target.com was discovered through certificate transparency logs and contained hardcoded credentials."
8. The Complete Toolchain in 2026
Here is the toolchain used by top-tier bug bounty hunters in 2026, with installation commands and purpose:
| Tool | Category | Purpose |
| -------------------- | --------------------- | ----------------------------------------------------------------- |
| **Subfinder** | Passive Recon | Collects subdomains from multiple passive sources |
| **Amass** | Passive + ASN Mapping | Advanced asset discovery, ASN enumeration, passive & active recon |
| **Assetfinder** | Passive Recon | Lightweight subdomain discovery |
| **Chaos** | Passive Recon | Uses ProjectDiscovery's asset intelligence dataset |
| **MassDNS** | Active DNS | High-speed DNS brute-force resolution |
| **Shuffledns** | Active DNS | MassDNS-based subdomain brute forcing with filtering |
| **Puredns** | Active DNS | Accurate brute-force with wildcard detection |
| **DNSx** | DNS Probing | DNS resolution, record checking, verification |
| **Httpx** | HTTP Probing | Detects live hosts, status codes, technologies |
| **FFUF** | Fuzzing | Directory, parameter & virtual host fuzzing |
| **Gobuster** | Brute Force | DNS and directory brute-force discovery |
| **Nuclei** | Vulnerability Scanner | Template-based vulnerability scanning |
| **Subzy** | Takeover Detection | Finds possible subdomain takeover issues |
| **DNSGen** | Permutation | Generates subdomain variations |
| **Gotator** | Permutation | Creates advanced wordlist mutations |
| **Cloud_enum** | Cloud Recon | Finds cloud assets across providers |
| **GAU (GetAllUrls)** | URL Discovery | Collects historical URLs from sources like Wayback |
| **Unfurl** | URL Parsing | Extracts domains, paths, parameters from URLs |
| **Mksub** | Wordlist Generation | Creates custom subdomain wordlists || Tool | Category | Purpose |
| -------------------- | --------------------- | ----------------------------------------------------------------- |
| **Subfinder** | Passive Recon | Collects subdomains from multiple passive sources |
| **Amass** | Passive + ASN Mapping | Advanced asset discovery, ASN enumeration, passive & active recon |
| **Assetfinder** | Passive Recon | Lightweight subdomain discovery |
| **Chaos** | Passive Recon | Uses ProjectDiscovery's asset intelligence dataset |
| **MassDNS** | Active DNS | High-speed DNS brute-force resolution |
| **Shuffledns** | Active DNS | MassDNS-based subdomain brute forcing with filtering |
| **Puredns** | Active DNS | Accurate brute-force with wildcard detection |
| **DNSx** | DNS Probing | DNS resolution, record checking, verification |
| **Httpx** | HTTP Probing | Detects live hosts, status codes, technologies |
| **FFUF** | Fuzzing | Directory, parameter & virtual host fuzzing |
| **Gobuster** | Brute Force | DNS and directory brute-force discovery |
| **Nuclei** | Vulnerability Scanner | Template-based vulnerability scanning |
| **Subzy** | Takeover Detection | Finds possible subdomain takeover issues |
| **DNSGen** | Permutation | Generates subdomain variations |
| **Gotator** | Permutation | Creates advanced wordlist mutations |
| **Cloud_enum** | Cloud Recon | Finds cloud assets across providers |
| **GAU (GetAllUrls)** | URL Discovery | Collects historical URLs from sources like Wayback |
| **Unfurl** | URL Parsing | Extracts domains, paths, parameters from URLs |
| **Mksub** | Wordlist Generation | Creates custom subdomain wordlists |Critical Configuration
API keys are not optional. Without them, you lose 60–70% of passive results. Configure:
~/.config/subfinder/provider-config.yaml:
securitytrails: [API_KEY]
virustotal: [API_KEY]
alienvault: [API_KEY]
censys: [USERNAME, SECRET]
shodan: [API_KEY]
github: [API_KEY]
urlscan: [API_KEY]securitytrails: [API_KEY]
virustotal: [API_KEY]
alienvault: [API_KEY]
censys: [USERNAME, SECRET]
shodan: [API_KEY]
github: [API_KEY]
urlscan: [API_KEY]9. Phase 1: Comprehensive Passive Enumeration — Hands-On
Step 1: Run Every Passive Tool
#!/bin/bash
DOMAIN=$1
mkdir -p recon/$DOMAIN/passive
# Subfinder
subfinder -d $DOMAIN -all -o recon/$DOMAIN/passive/subfinder.txt
# Amass passive
amass enum -passive -d $DOMAIN -o recon/$DOMAIN/passive/amass.txt
# Assetfinder
assetfinder --subs-only $DOMAIN > recon/$DOMAIN/passive/assetfinder.txt
# Chaos
chaos -d $DOMAIN -o recon/$DOMAIN/passive/chaos.txt
# crt.sh (deep query)
curl -s "https://crt.sh/?q=%25.$DOMAIN&output=json" | \
jq -r '.[].name_value' | sed 's/\*\.//g' | sort -u > recon/$DOMAIN/passive/crtsh.txt
# Wayback Machine via gau
gau --subs $DOMAIN | unfurl -u domains | sort -u > recon/$DOMAIN/passive/gau.txt
# Merge
cat recon/$DOMAIN/passive/*.txt | sort -u > recon/$DOMAIN/passive/all_passive.txt
echo "Passive subs: $(wc -l < recon/$DOMAIN/passive/all_passive.txt)"#!/bin/bash
DOMAIN=$1
mkdir -p recon/$DOMAIN/passive
# Subfinder
subfinder -d $DOMAIN -all -o recon/$DOMAIN/passive/subfinder.txt
# Amass passive
amass enum -passive -d $DOMAIN -o recon/$DOMAIN/passive/amass.txt
# Assetfinder
assetfinder --subs-only $DOMAIN > recon/$DOMAIN/passive/assetfinder.txt
# Chaos
chaos -d $DOMAIN -o recon/$DOMAIN/passive/chaos.txt
# crt.sh (deep query)
curl -s "https://crt.sh/?q=%25.$DOMAIN&output=json" | \
jq -r '.[].name_value' | sed 's/\*\.//g' | sort -u > recon/$DOMAIN/passive/crtsh.txt
# Wayback Machine via gau
gau --subs $DOMAIN | unfurl -u domains | sort -u > recon/$DOMAIN/passive/gau.txt
# Merge
cat recon/$DOMAIN/passive/*.txt | sort -u > recon/$DOMAIN/passive/all_passive.txt
echo "Passive subs: $(wc -l < recon/$DOMAIN/passive/all_passive.txt)"Step 2: Deep-Dive CT Log Mining
Certificate Transparency logs are the single richest passive source. Beyond the basic crt.sh query, try:
# CertSpotter API
curl -s "https://api.certspotter.com/v1/issuances?domain=$DOMAIN&include_subdomains=true&expand=dns_names" | \
jq -r '.[].dns_names[]' | sort -u
# Google CT log (alternative endpoint)
curl -s "https://transparencyreport.google.com/transparencyreport/api/v3/httpsreport/ct/certsearch?include_subdomains=true&domain=$DOMAIN"# CertSpotter API
curl -s "https://api.certspotter.com/v1/issuances?domain=$DOMAIN&include_subdomains=true&expand=dns_names" | \
jq -r '.[].dns_names[]' | sort -u
# Google CT log (alternative endpoint)
curl -s "https://transparencyreport.google.com/transparencyreport/api/v3/httpsreport/ct/certsearch?include_subdomains=true&domain=$DOMAIN"Step 3: GitHub and Code Search
# Search GitHub for domain references (requires token)
gh search code "domain.com" --limit 1000 | grep -oP '[a-zA-Z0-9_-]+\.domain\.com' | sort -u
# Search GitLab, Bitbucket, and public S3 buckets
# Use truffleHog or gitleaks after finding repos# Search GitHub for domain references (requires token)
gh search code "domain.com" --limit 1000 | grep -oP '[a-zA-Z0-9_-]+\.domain\.com' | sort -u
# Search GitLab, Bitbucket, and public S3 buckets
# Use truffleHog or gitleaks after finding repos10. Phase 2: Active Brute-Force — From Theory to Shell
Why You Need Active Enumeration
Passive sources will never give you everything. Consider vpn.target.com, jenkins.target.com, grafana.target.com — these are rarely indexed by third-party services. They're internal-facing, known only to employees, and only discoverable through brute-force.
Step 1: Build Your Resolver List
git clone https://github.com/vortexau/dnsvalidator.git
cd dnsvalidator
python3 dnsvalidator.py -t https://public-dns.info/nameservers.txt -o resolvers.txtgit clone https://github.com/vortexau/dnsvalidator.git
cd dnsvalidator
python3 dnsvalidator.py -t https://public-dns.info/nameservers.txt -o resolvers.txtWhy this matters: Free public resolvers from Google (8.8.8.8) or Cloudflare (1.1.1.1) rate-limit aggressively. A custom resolver list of 500–1,000 verified resolvers yields 10x the throughput.
Step 2: Prepare Wordlists
# Download the gold standard lists
wget https://raw.githubusercontent.com/danielmiessler/SecLists/master/Discovery/DNS/subdomains-top1million-5000.txt
wget https://raw.githubusercontent.com/danielmiessler/SecLists/master/Discovery/DNS/bitquark-subdomains-top100000.txt
wget https://raw.githubusercontent.com/assetnote/commonspeak2/master/wordlists/subdomains/subdomains.txt
# Also use the famous "all.txt" (2M+ entries) from jhaddix
wget https://raw.githubusercontent.com/jhaddix/domain/master/all.txt# Download the gold standard lists
wget https://raw.githubusercontent.com/danielmiessler/SecLists/master/Discovery/DNS/subdomains-top1million-5000.txt
wget https://raw.githubusercontent.com/danielmiessler/SecLists/master/Discovery/DNS/bitquark-subdomains-top100000.txt
wget https://raw.githubusercontent.com/assetnote/commonspeak2/master/wordlists/subdomains/subdomains.txt
# Also use the famous "all.txt" (2M+ entries) from jhaddix
wget https://raw.githubusercontent.com/jhaddix/domain/master/all.txtStep 3: Run Puredns (Recommended over raw MassDNS)
puredns bruteforce ~/wordlists/combined_subs.txt $DOMAIN \
-r resolvers.txt \
-o recon/$DOMAIN/active/brute.txt \
--wildcard-batch 1000000puredns bruteforce ~/wordlists/combined_subs.txt $DOMAIN \
-r resolvers.txt \
-o recon/$DOMAIN/active/brute.txt \
--wildcard-batch 1000000Why Puredns over raw MassDNS: Puredns includes intelligent wildcard detection. A wildcard DNS entry (*.target.com → 10.0.0.1`) can cause MassDNS to report thousands of false positives. Puredns detects the wildcard pattern and filters those results automatically.
Step 4: Gobuster for Quick Scans
gobuster dns -d $DOMAIN -w ~/wordlists/subdomains-top1million-5000.txt -t 50gobuster dns -d $DOMAIN -w ~/wordlists/subdomains-top1million-5000.txt -t 5011. Phase 3: Permutation and Mutation Attacks
Once you have discovered subdomains, you can generate variations based on observed naming patterns. This is where intermediate hunters become advanced hunters.
DNSGen — Simple Permutation Engine
cat recon/$DOMAIN/passive/all_passive.txt recon/$DOMAIN/active/brute.txt | sort -u > discovered.txt
dnsgen discovered.txt | puredns resolve -r resolvers.txt -o recon/$DOMAIN/active/permutations.txtcat recon/$DOMAIN/passive/all_passive.txt recon/$DOMAIN/active/brute.txt | sort -u > discovered.txt
dnsgen discovered.txt | puredns resolve -r resolvers.txt -o recon/$DOMAIN/active/permutations.txtDNSGen generates permutations like:
api-dev→dev-api,api-dev-2,api-dev-stagingadmin→admin1,admin2,admin-backup
Gotator — Advanced Mutation Engine
gotator -sub discovered.txt \
-perm ~/wordlists/permutations_list.txt \
-depth 1 \
-numbers 10 \
-mindup \
-adv \
-md \
-fast | \
sort -u | \
puredns resolve -r resolvers.txt -o recon/$DOMAIN/active/gotator_perms.txtgotator -sub discovered.txt \
-perm ~/wordlists/permutations_list.txt \
-depth 1 \
-numbers 10 \
-mindup \
-adv \
-md \
-fast | \
sort -u | \
puredns resolve -r resolvers.txt -o recon/$DOMAIN/active/gotator_perms.txtGotator's flags explained:
-depth 1: Permutation depth (how many mutations deep)-numbers 10: Append numbers 0-10 to each word-mindup: Remove duplicates-adv: Advanced mode (more aggressive mutations)-md: Markov chain mode (predicts likely subdomain names)
Mksub — Custom Pattern Generation
From Trickest, mksub generates subdomains based on observed patterns:
cat discovered.txt | mksub -patterns ~/wordlists/patterns.txt > mksub_results.txtcat discovered.txt | mksub -patterns ~/wordlists/patterns.txt > mksub_results.txtReal-world pattern example: If you find api-us-east-1.target.com, you can generate api-us-west-2.target.com, api-eu-west-1.target.com, api-ap-southeast-1.target.com — and you'll often find they all exist.
12. Phase 4: ASN-Based and Infrastructure-Aware Enumeration
Large organizations own entire blocks of IP addresses. If you find one subdomain, you can find them all by mapping the ASN.
Find ASN Numbers
# Using whois
whois -h whois.radb.net -- '-i origin AS12345' | grep -E '^route:' | awk '{print $2}'
# Using Amass
amass intel -asn AS12345 -o asn_subs.txt
# Using BGP lookup
curl -s "https://bgp.he.net/net/AS12345" | grep -oP '[\d.]+/\d+' | sort -u# Using whois
whois -h whois.radb.net -- '-i origin AS12345' | grep -E '^route:' | awk '{print $2}'
# Using Amass
amass intel -asn AS12345 -o asn_subs.txt
# Using BGP lookup
curl -s "https://bgp.he.net/net/AS12345" | grep -oP '[\d.]+/\d+' | sort -uReverse DNS on IP Ranges
# Generate IPs from CIDR ranges using mapcidr
echo "203.0.113.0/24" | mapcidr -silent > ips.txt
# Reverse DNS lookup
cat ips.txt | dnsx -ptr -resp-only -o reverse_dns.txt
# These are subdomains that no forward DNS query would find# Generate IPs from CIDR ranges using mapcidr
echo "203.0.113.0/24" | mapcidr -silent > ips.txt
# Reverse DNS lookup
cat ips.txt | dnsx -ptr -resp-only -o reverse_dns.txt
# These are subdomains that no forward DNS query would findReal-world application: A hunter targeting a SaaS company found their ASN, reverse-DNS'd the entire IP range, and discovered internal-logging.target.com — a Graylog instance with no authentication.
13. Phase 5: Virtual Host Fuzzing
Some subdomains don't have DNS records but still serve content. They exist as virtual hosts on a shared web server.
# FFUF vhost fuzzing
ffuf -c \
-u "https://TARGET_IP" \
-H "Host: FUZZ.target.com" \
-w ~/wordlists/virtual-hosts.txt \
-fc 404 \
-fs 0 \
-t 100# FFUF vhost fuzzing
ffuf -c \
-u "https://TARGET_IP" \
-H "Host: FUZZ.target.com" \
-w ~/wordlists/virtual-hosts.txt \
-fc 404 \
-fs 0 \
-t 100What to look for: Different response sizes or status codes indicate different virtual hosts. A 200 with 15,000 bytes is likely a different application than a 200 with 3,000 bytes.
# More detailed ffuf with content-length filtering
ffuf -c \
-u "https://TARGET_IP" \
-H "Host: FUZZ.target.com" \
-w ~/wordlists/virtual-hosts.txt \
-mc all \
-ac # Automatically calibrate filter# More detailed ffuf with content-length filtering
ffuf -c \
-u "https://TARGET_IP" \
-H "Host: FUZZ.target.com" \
-w ~/wordlists/virtual-hosts.txt \
-mc all \
-ac # Automatically calibrate filter14. Phase 6: Subdomain Takeover Detection and Exploitation
This is where subdomain enumeration directly translates to bounty dollars. Subdomain takeover consistently ranks among the most common high-severity findings in bug bounty.
Automated Detection
# Subzy — quick scan
subzy run --targets recon/$DOMAIN/all_subs.txt -o recon/$DOMAIN/takeovers/subzy.txt
# Nuclei — accurate scan with community templates
nuclei -l recon/$DOMAIN/all_subs.txt \
-t ~/nuclei-templates/http/takeovers/ \
-o recon/$DOMAIN/takeovers/nuclei_takeovers.txt# Subzy — quick scan
subzy run --targets recon/$DOMAIN/all_subs.txt -o recon/$DOMAIN/takeovers/subzy.txt
# Nuclei — accurate scan with community templates
nuclei -l recon/$DOMAIN/all_subs.txt \
-t ~/nuclei-templates/http/takeovers/ \
-o recon/$DOMAIN/takeovers/nuclei_takeovers.txtService Fingerprints — The Critical Reference
The definitive fingerprint database is can-i-take-over-xyz. Every bug hunter should be intimately familiar with the fingerprints for the top 20 takeover-vulnerable services:
Manual Verification — The Gold Standard
Never rely on automated tools alone. Manual verification is mandatory before reporting:
# Step 1: Check the CNAME record
dig CNAME subdomain.target.com +short
# Step 2: Check HTTP response
curl -I "http://subdomain.target.com" 2>/dev/null | head -20
curl -I "https://subdomain.target.com" 2>/dev/null | head -20
# Step 3: Check full response body
curl -s "http://subdomain.target.com" | head -50
# Step 4: Verify service-specific fingerprint
# For S3:
curl -s "http://subdomain.target.com" | grep -i "nosuchbucket"
# For Heroku:
curl -s "http://subdomain.target.com" | grep -i "no such app"
# For GitHub Pages:
curl -s "http://subdomain.target.com" | grep -i "there isn't a github pages site"# Step 1: Check the CNAME record
dig CNAME subdomain.target.com +short
# Step 2: Check HTTP response
curl -I "http://subdomain.target.com" 2>/dev/null | head -20
curl -I "https://subdomain.target.com" 2>/dev/null | head -20
# Step 3: Check full response body
curl -s "http://subdomain.target.com" | head -50
# Step 4: Verify service-specific fingerprint
# For S3:
curl -s "http://subdomain.target.com" | grep -i "nosuchbucket"
# For Heroku:
curl -s "http://subdomain.target.com" | grep -i "no such app"
# For GitHub Pages:
curl -s "http://subdomain.target.com" | grep -i "there isn't a github pages site"The Freshdesk Zero-Day — Creating a Custom Fingerprint
The famous Freshdesk case study showed the power of custom fingerprint creation. Here's how you can replicate that methodology:
# Step 1: Register a Freshdesk trial account
# Step 2: Set up a custom domain
# Step 3: Delete the Freshdesk instance
# Step 4: Note the 404 response
curl -s "http://your-subdomain.freshdesk.com" | tee freshdesk_fingerprint.txt
# Step 5: Create a Nuclei template
cat > freshdesk-custom-takeover.yaml << 'EOF'
id: freshdesk-custom-takeover
info:
name: Freshdesk Subdomain Takeover
author: hunter
severity: high
description: Detects dangling Freshdesk subdomains
requests:
- method: GET
path:
- "{{BaseURL}}"
matchers:
- type: word
words:
- "The page you were looking for doesn't exist"
- "We couldn't find the page you requested"
condition: or
EOF
# Step 6: Scan targets
nuclei -l subdomains.txt -t freshdesk-custom-takeover.yaml# Step 1: Register a Freshdesk trial account
# Step 2: Set up a custom domain
# Step 3: Delete the Freshdesk instance
# Step 4: Note the 404 response
curl -s "http://your-subdomain.freshdesk.com" | tee freshdesk_fingerprint.txt
# Step 5: Create a Nuclei template
cat > freshdesk-custom-takeover.yaml << 'EOF'
id: freshdesk-custom-takeover
info:
name: Freshdesk Subdomain Takeover
author: hunter
severity: high
description: Detects dangling Freshdesk subdomains
requests:
- method: GET
path:
- "{{BaseURL}}"
matchers:
- type: word
words:
- "The page you were looking for doesn't exist"
- "We couldn't find the page you requested"
condition: or
EOF
# Step 6: Scan targets
nuclei -l subdomains.txt -t freshdesk-custom-takeover.yamlThe Proof of Concept — Do It Right
Once you confirm a takeover, do not deface the page. The industry best practice is:
- Create a hidden file at a non-obvious path (e.g.,
/hackerone-username.txt) - Place your HackerOne handle inside an HTML comment in that file
- Screenshot the evidence
- Email the program with the hidden path
Example:
<!-- Security vulnerability report by @yourhandle --><!-- Security vulnerability report by @yourhandle -->This demonstrates control without disrupting users or brand trust.
15. What Happens After Takeover? CORS, Cookies, OAuth, and CSP Chaining
This is the section that separates beginners from advanced hunters. Once you control a subdomain, the impact chain extends far beyond just hosting content.
Cookie Manipulation
// From the hijacked subdomain, set a cookie on the parent domain
document.cookie = "session=attacker_session; domain=.target.com; path=/";// From the hijacked subdomain, set a cookie on the parent domain
document.cookie = "session=attacker_session; domain=.target.com; path=/";This works because browsers treat subdomains as "same site" for cookie purposes. If the main site's cookies are not HttpOnly, you can read them. Even if they are HttpOnly, you can overwrite them with your own values.
CORS Abuse
If target.com's CORS policy trusts *.target.com:
GET /api/user/profile HTTP/1.1
Host: target.com
Origin: https://hijacked-subdomain.target.com
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://hijacked-subdomain.target.com
Access-Control-Allow-Credentials: trueGET /api/user/profile HTTP/1.1
Host: target.com
Origin: https://hijacked-subdomain.target.com
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://hijacked-subdomain.target.com
Access-Control-Allow-Credentials: trueThis allows you to exfiltrate authenticated data from the main application via JavaScript running on the hijacked subdomain.
OAuth Redirect Abuse
If the OAuth implementation allows *.target.com as a redirect URI:
https://accounts.google.com/o/oauth2/auth?
client_id=xxxxx&
redirect_uri=https://hijacked-subdomain.target.com/oauth/callback&
response_type=code&
scope=openid%20email&
state=attacker_statehttps://accounts.google.com/o/oauth2/auth?
client_id=xxxxx&
redirect_uri=https://hijacked-subdomain.target.com/oauth/callback&
response_type=code&
scope=openid%20email&
state=attacker_stateWhen the victim authenticates, the OAuth token (or authorization code) is forwarded to the hijacked subdomain, where the attacker captures it.
CSP Bypass
If the Content Security Policy includes the hijacked subdomain:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://hijacked-subdomain.target.com;Content-Security-Policy:
default-src 'self';
script-src 'self' https://hijacked-subdomain.target.com;You can now:
<script src="https://hijacked-subdomain.target.com/evil.js"></script><script src="https://hijacked-subdomain.target.com/evil.js"></script>And evil.js runs in the context of the main application, bypassing CSP entirely.
16. The Production-Grade Automated Pipeline
Here's the complete, battle-tested pipeline that top hunters use in 2026. This is not theoretical — this exact workflow has produced thousands of valid bug reports.
#!/bin/bash
# ultimate_sub_enum.sh — Complete subdomain enumeration pipeline
# Usage: ./ultimate_sub_enum.sh target.com
set -euo pipefail
DOMAIN=$1
RESOLVERS=~/tools/resolvers.txt
WORDLIST=~/wordlists/master_subs.txt
PERM_LIST=~/wordlists/permutations.txt
OUTPUT_DIR=recon/$DOMAIN
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $OUTPUT_DIR/{passive,active,takeovers,live}
echo "=========================================="
echo " Subdomain Enumeration Pipeline"
echo " Target: $DOMAIN"
echo " Date: $DATE"
echo "=========================================="
# =============================================
# PHASE 1: PASSIVE ENUMERATION
# =============================================
echo ""
echo "[+] Phase 1: Passive Enumeration"
echo " Running Subfinder..."
subfinder -d $DOMAIN -all -o $OUTPUT_DIR/passive/subfinder.txt
echo " Running Amass (passive)..."
amass enum -passive -d $DOMAIN -o $OUTPUT_DIR/passive/amass.txt
echo " Running Assetfinder..."
assetfinder --subs-only $DOMAIN > $OUTPUT_DIR/passive/assetfinder.txt
echo " Running Chaos..."
chaos -d $DOMAIN -o $OUTPUT_DIR/passive/chaos.txt 2>/dev/null || true
echo " Querying crt.sh..."
curl -s "https://crt.sh/?q=%25.$DOMAIN&output=json" 2>/dev/null | \
jq -r '.[].name_value' 2>/dev/null | sed 's/\*\.//g' | sort -u > $OUTPUT_DIR/passive/crtsh.txt
echo " Querying Wayback Machine..."
gau --subs $DOMAIN 2>/dev/null | unfurl -u domains | sort -u > $OUTPUT_DIR/passive/gau.txt
echo " Merging passive results..."
cat $OUTPUT_DIR/passive/*.txt | sort -u > $OUTPUT_DIR/passive/all_passive.txt
PASSIVE_COUNT=$(wc -l < $OUTPUT_DIR/passive/all_passive.txt)
echo " Passive subdomains found: $PASSIVE_COUNT"
# =============================================
# PHASE 2: ACTIVE BRUTE-FORCE
# =============================================
echo ""
echo "[+] Phase 2: Active Brute-Force"
echo " Running Puredns (bruteforce)..."
puredns bruteforce $WORDLIST $DOMAIN \
-r $RESOLVERS \
-o $OUTPUT_DIR/active/brute.txt \
--wildcard-batch 1000000 \
--rate-limit 1000 2>/dev/null
BRUTE_COUNT=$(wc -l < $OUTPUT_DIR/active/brute.txt 2>/dev/null || echo 0)
echo " Brute-force subs found: $BRUTE_COUNT"
# =============================================
# PHASE 3: PERMUTATION ATTACK
# =============================================
echo ""
echo "[+] Phase 3: Permutation Attack"
echo " Generating permutations with dnsgen..."
cat $OUTPUT_DIR/passive/all_passive.txt $OUTPUT_DIR/active/brute.txt 2>/dev/null | \
sort -u > $OUTPUT_DIR/discovered.txt
cat $OUTPUT_DIR/discovered.txt | dnsgen - 2>/dev/null | \
puredns resolve -r $RESOLVERS --rate-limit 500 \
-o $OUTPUT_DIR/active/dnsgen_perms.txt 2>/dev/null || true
echo " Generating permutations with gotator..."
gotator -sub $OUTPUT_DIR/discovered.txt \
-perm $PERM_LIST \
-depth 1 -numbers 10 -mindup -adv -md -fast 2>/dev/null | \
sort -u | \
puredns resolve -r $RESOLVERS --rate-limit 500 \
-o $OUTPUT_DIR/active/gotator_perms.txt 2>/dev/null || true
PERM_COUNT=$(wc -l < $OUTPUT_DIR/active/dnsgen_perms.txt 2>/dev/null || echo 0)
GOT_COUNT=$(wc -l < $OUTPUT_DIR/active/gotator_perms.txt 2>/dev/null || echo 0)
echo " New permutations: dnsgen=$PERM_COUNT, gotator=$GOT_COUNT"
# =============================================
# PHASE 4: MERGE ALL RESULTS
# =============================================
echo ""
echo "[+] Phase 4: Merging All Results"
cat $OUTPUT_DIR/passive/all_passive.txt \
$OUTPUT_DIR/active/brute.txt \
$OUTPUT_DIR/active/dnsgen_perms.txt \
$OUTPUT_DIR/active/gotator_perms.txt \
2>/dev/null | sort -u > $OUTPUT_DIR/all_subs.txt
TOTAL_COUNT=$(wc -l < $OUTPUT_DIR/all_subs.txt)
echo " Total unique subdomains: $TOTAL_COUNT"
# =============================================
# PHASE 5: LIVE HOST VALIDATION
# =============================================
echo ""
echo "[+] Phase 5: Live Host Validation"
echo " DNS resolution with dnsx..."
cat $OUTPUT_DIR/all_subs.txt | \
dnsx -a -resp-only -t 100 \
-o $OUTPUT_DIR/live/resolved_ips.txt 2>/dev/null
echo " HTTP probing with httpx..."
cat $OUTPUT_DIR/all_subs.txt | \
httpx -silent \
-title \
-status-code \
-tech-detect \
-content-length \
-follow-redirects \
-web-server \
-o $OUTPUT_DIR/live/live_hosts.txt 2>/dev/null
LIVE_COUNT=$(wc -l < $OUTPUT_DIR/live/live_hosts.txt 2>/dev/null || echo 0)
echo " Live hosts: $LIVE_COUNT"
# =============================================
# PHASE 6: SUBDOMAIN TAKEOVER DETECTION
# =============================================
echo ""
echo "[+] Phase 6: Subdomain Takeover Detection"
echo " Running Subzy..."
subzy run --targets $OUTPUT_DIR/all_subs.txt \
--concurrency 100 \
--timeout 10 \
--verify \
--hide-fails \
--output $OUTPUT_DIR/takeovers/subzy_results.json 2>/dev/null || true
echo " Running Nuclei (takeover templates)..."
nuclei -l $OUTPUT_DIR/all_subs.txt \
-t ~/nuclei-templates/http/takeovers/ \
-o $OUTPUT_DIR/takeovers/nuclei_takeovers.txt \
-c 50 \
-timeout 10 2>/dev/null || true
TAKEOVER_COUNT=$(wc -l < $OUTPUT_DIR/takeovers/nuclei_takeovers.txt 2>/dev/null || echo 0)
echo " Takeover candidates: $TAKEOVER_COUNT"
# =============================================
# SUMMARY
# =============================================
echo ""
echo "=========================================="
echo " RECON COMPLETE"
echo " Target: $DOMAIN"
echo " Passive: $PASSIVE_COUNT"
echo " Brute-force: $BRUTE_COUNT"
echo " Permutations:$((PERM_COUNT + GOT_COUNT))"
echo " ----------------------------------------"
echo " Total: $TOTAL_COUNT"
echo " Live: $LIVE_COUNT"
echo " Takeovers: $TAKEOVER_COUNT"
echo "=========================================="
echo " Results in: $OUTPUT_DIR/"#!/bin/bash
# ultimate_sub_enum.sh — Complete subdomain enumeration pipeline
# Usage: ./ultimate_sub_enum.sh target.com
set -euo pipefail
DOMAIN=$1
RESOLVERS=~/tools/resolvers.txt
WORDLIST=~/wordlists/master_subs.txt
PERM_LIST=~/wordlists/permutations.txt
OUTPUT_DIR=recon/$DOMAIN
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $OUTPUT_DIR/{passive,active,takeovers,live}
echo "=========================================="
echo " Subdomain Enumeration Pipeline"
echo " Target: $DOMAIN"
echo " Date: $DATE"
echo "=========================================="
# =============================================
# PHASE 1: PASSIVE ENUMERATION
# =============================================
echo ""
echo "[+] Phase 1: Passive Enumeration"
echo " Running Subfinder..."
subfinder -d $DOMAIN -all -o $OUTPUT_DIR/passive/subfinder.txt
echo " Running Amass (passive)..."
amass enum -passive -d $DOMAIN -o $OUTPUT_DIR/passive/amass.txt
echo " Running Assetfinder..."
assetfinder --subs-only $DOMAIN > $OUTPUT_DIR/passive/assetfinder.txt
echo " Running Chaos..."
chaos -d $DOMAIN -o $OUTPUT_DIR/passive/chaos.txt 2>/dev/null || true
echo " Querying crt.sh..."
curl -s "https://crt.sh/?q=%25.$DOMAIN&output=json" 2>/dev/null | \
jq -r '.[].name_value' 2>/dev/null | sed 's/\*\.//g' | sort -u > $OUTPUT_DIR/passive/crtsh.txt
echo " Querying Wayback Machine..."
gau --subs $DOMAIN 2>/dev/null | unfurl -u domains | sort -u > $OUTPUT_DIR/passive/gau.txt
echo " Merging passive results..."
cat $OUTPUT_DIR/passive/*.txt | sort -u > $OUTPUT_DIR/passive/all_passive.txt
PASSIVE_COUNT=$(wc -l < $OUTPUT_DIR/passive/all_passive.txt)
echo " Passive subdomains found: $PASSIVE_COUNT"
# =============================================
# PHASE 2: ACTIVE BRUTE-FORCE
# =============================================
echo ""
echo "[+] Phase 2: Active Brute-Force"
echo " Running Puredns (bruteforce)..."
puredns bruteforce $WORDLIST $DOMAIN \
-r $RESOLVERS \
-o $OUTPUT_DIR/active/brute.txt \
--wildcard-batch 1000000 \
--rate-limit 1000 2>/dev/null
BRUTE_COUNT=$(wc -l < $OUTPUT_DIR/active/brute.txt 2>/dev/null || echo 0)
echo " Brute-force subs found: $BRUTE_COUNT"
# =============================================
# PHASE 3: PERMUTATION ATTACK
# =============================================
echo ""
echo "[+] Phase 3: Permutation Attack"
echo " Generating permutations with dnsgen..."
cat $OUTPUT_DIR/passive/all_passive.txt $OUTPUT_DIR/active/brute.txt 2>/dev/null | \
sort -u > $OUTPUT_DIR/discovered.txt
cat $OUTPUT_DIR/discovered.txt | dnsgen - 2>/dev/null | \
puredns resolve -r $RESOLVERS --rate-limit 500 \
-o $OUTPUT_DIR/active/dnsgen_perms.txt 2>/dev/null || true
echo " Generating permutations with gotator..."
gotator -sub $OUTPUT_DIR/discovered.txt \
-perm $PERM_LIST \
-depth 1 -numbers 10 -mindup -adv -md -fast 2>/dev/null | \
sort -u | \
puredns resolve -r $RESOLVERS --rate-limit 500 \
-o $OUTPUT_DIR/active/gotator_perms.txt 2>/dev/null || true
PERM_COUNT=$(wc -l < $OUTPUT_DIR/active/dnsgen_perms.txt 2>/dev/null || echo 0)
GOT_COUNT=$(wc -l < $OUTPUT_DIR/active/gotator_perms.txt 2>/dev/null || echo 0)
echo " New permutations: dnsgen=$PERM_COUNT, gotator=$GOT_COUNT"
# =============================================
# PHASE 4: MERGE ALL RESULTS
# =============================================
echo ""
echo "[+] Phase 4: Merging All Results"
cat $OUTPUT_DIR/passive/all_passive.txt \
$OUTPUT_DIR/active/brute.txt \
$OUTPUT_DIR/active/dnsgen_perms.txt \
$OUTPUT_DIR/active/gotator_perms.txt \
2>/dev/null | sort -u > $OUTPUT_DIR/all_subs.txt
TOTAL_COUNT=$(wc -l < $OUTPUT_DIR/all_subs.txt)
echo " Total unique subdomains: $TOTAL_COUNT"
# =============================================
# PHASE 5: LIVE HOST VALIDATION
# =============================================
echo ""
echo "[+] Phase 5: Live Host Validation"
echo " DNS resolution with dnsx..."
cat $OUTPUT_DIR/all_subs.txt | \
dnsx -a -resp-only -t 100 \
-o $OUTPUT_DIR/live/resolved_ips.txt 2>/dev/null
echo " HTTP probing with httpx..."
cat $OUTPUT_DIR/all_subs.txt | \
httpx -silent \
-title \
-status-code \
-tech-detect \
-content-length \
-follow-redirects \
-web-server \
-o $OUTPUT_DIR/live/live_hosts.txt 2>/dev/null
LIVE_COUNT=$(wc -l < $OUTPUT_DIR/live/live_hosts.txt 2>/dev/null || echo 0)
echo " Live hosts: $LIVE_COUNT"
# =============================================
# PHASE 6: SUBDOMAIN TAKEOVER DETECTION
# =============================================
echo ""
echo "[+] Phase 6: Subdomain Takeover Detection"
echo " Running Subzy..."
subzy run --targets $OUTPUT_DIR/all_subs.txt \
--concurrency 100 \
--timeout 10 \
--verify \
--hide-fails \
--output $OUTPUT_DIR/takeovers/subzy_results.json 2>/dev/null || true
echo " Running Nuclei (takeover templates)..."
nuclei -l $OUTPUT_DIR/all_subs.txt \
-t ~/nuclei-templates/http/takeovers/ \
-o $OUTPUT_DIR/takeovers/nuclei_takeovers.txt \
-c 50 \
-timeout 10 2>/dev/null || true
TAKEOVER_COUNT=$(wc -l < $OUTPUT_DIR/takeovers/nuclei_takeovers.txt 2>/dev/null || echo 0)
echo " Takeover candidates: $TAKEOVER_COUNT"
# =============================================
# SUMMARY
# =============================================
echo ""
echo "=========================================="
echo " RECON COMPLETE"
echo " Target: $DOMAIN"
echo " Passive: $PASSIVE_COUNT"
echo " Brute-force: $BRUTE_COUNT"
echo " Permutations:$((PERM_COUNT + GOT_COUNT))"
echo " ----------------------------------------"
echo " Total: $TOTAL_COUNT"
echo " Live: $LIVE_COUNT"
echo " Takeovers: $TAKEOVER_COUNT"
echo "=========================================="
echo " Results in: $OUTPUT_DIR/"17. Lessons from the Trenches — What the Best Hunters Do Differently
Lesson 1: They Don't Rely on a Single Tool
The ATG case study proved this. Subfinder, crt.sh, the Wayback Machine, and Gobuster each found unique subdomains that the others missed. The merged list was always larger than any individual output.
Rule of thumb: If you run fewer than 5 tools, you're missing at least 40% of discoverable subdomains.
Lesson 2: They Build Custom Wordlists
Generic wordlists from SecLists are a starting point, not the destination. Top hunters analyze their target's naming conventions from discovered subdomains, then craft custom wordlists:
# Extract common prefixes from discovered subdomains
cat discovered.txt | sed 's/\.target\.com//' | sed 's/[0-9\-]//g' | sort | uniq -c | sort -rn | head -50# Extract common prefixes from discovered subdomains
cat discovered.txt | sed 's/\.target\.com//' | sed 's/[0-9\-]//g' | sort | uniq -c | sort -rn | head -50If you see api, api-dev, api-staging, api-qa, add api-uat, api-sandbox, api-demo, api-test, api-production to your wordlist.
Lesson 3: They Hunt on Weekends and Holidays
The $5,000 USDC blockchain takeover was discovered during a weekend scan. Why? Because companies often decommission services during low-traffic periods (Friday nights, holiday weekends) and forget to clean up DNS records.
Lesson 4: They Fingerprint Services Before They're Public
The Freshdesk zero-day case proves this. The hunter who finds a new fingerprint for an undocumented service has zero competition for those bounties. The methodology is straightforward:
- Sign up for every SaaS platform you can find
- Configure a custom domain
- Delete the instance
- Note the error message
- Create a Nuclei template
- Scan the Alexa Top 1 Million
Lesson 5: They Validate Manually Before Reporting
Subzy and Nuclei produce false positives. The $5,000 blockchain payout didn't happen because a tool said "vulnerable" — it happened because the hunter manually verified:
- The CNAME was correct (
dig CNAME) - The error matched the fingerprint (
curl -I) - The service was actually claimable (registered the bucket)
- The impact was real (hosted a PoC)
Lesson 6: They Chain Subdomain Takeovers Into Higher-Impact Findings
A subdomain takeover is rarely the end of the chain. It's the entry point for:
- Session hijacking via cookie manipulation
- Data exfiltration via CORS
- Credential theft via OAuth redirect
- CSP bypass for XSS on the main domain
- Phishing under a trusted domain
The best reporters don't just say "I can host content on subdomain.target.com". They say "I can steal session cookies from app.target.com because of this takeover."
18. Final Words
Subdomain enumeration in 2026 is not a single technique or a single tool. It is a pipeline — a layered, automated, continuously-improving process that combines:
- Passive intelligence from 30+ third-party sources
- Active brute-force with multi-million-entry wordlists and intelligent wildcard detection
- Permutation engines that infer naming conventions
- ASN-aware infrastructure mapping that discovers assets through IP ownership
- Virtual host fuzzing that finds subdomains with no DNS records
- Takeover detection using fingerprint databases and custom templates
- Impact chaining that turns a dangling DNS record into a critical-severity finding
The hunters who consistently earn top bounties are not the ones who know more about SQL injection or XSS. They are the ones who have mapped the entire attack surface before anyone else has even found the front door.
Every subdomain is a potential bounty. Every dangling CNAME is a potential payout. Every forgotten staging server is a potential critical vulnerability.
Go find them.
References and Further Reading
- YesWeHack — Subdomain Enumeration: Expand Attack Surfaces
- HackerOne — A Guide to Subdomain Takeovers 2.0
- OWASP — Test for Subdomain Takeover (WSTG)
- ProjectDiscovery — Recon 102: Subdomain Enumeration
- Can I Take Over XYZ — The definitive fingerprint database
- SecLists — DNS Subdomain Wordlists
- Outpost24 — The Dangerous Art of Subdomain Enumeration
- MDN Web Docs — Subdomain Takeover*`
GitHub: SecurityTalent | Medium: Security Talent | Twitter: Securi3yTalent | Facebook: Securi3ytalent | Telegram: Securi3yTalent
#CyberSecurity #BugBounty #BugBountyHunter #WebSecurity #EthicalHacking #OSINT #Recon #SubdomainEnumeration #PenTesting #InfoSec #AIsecurity #AttackSurfaceManagement #CloudSecurity #APIsecurity #SecurityResearch #ZeroDay #VulnerabilityManagement