September 4, 2026
The Bug Bounty Recon Cheat Sheet: A Practical Workflow From Domain to Attack Surface
Reconnaissance is the part of bug bounty hunting that separates the people who find bugs consistently from the people who get lucky onceβ¦

By T4nv1
4 min read
Reconnaissance is the part of bug bounty hunting that separates the people who find bugs consistently from the people who get lucky once. Most hunters skip straight to scanning for vulnerabilities and miss half the attack surface because they never mapped it properly in the first place.
This is the recon workflow I actually use, broken into stages, with the commands and tools for each one. Bookmark it, keep it open in a second tab while you're hunting, and adapt it to whatever program you're working on.
Why Recon Matters More Than Most Hunters Think
Every subdomain you miss is an asset someone else might find first. Every forgotten staging environment, exposed admin panel, or old API version is a potential entry point. Good recon isn't a box to check before "real" testing starts β it is the testing. The bugs are usually sitting in the parts of the attack surface that nobody bothered to map.
The workflow below moves from broad to narrow: start with the whole domain, work down to subdomains, then live hosts, then endpoints, then parameters, then the actual testing surface.
Stage 1: Scope and Passive Groundwork
Before running a single tool, read the program's scope page twice. Note:
- In-scope domains and wildcards (
*.example.comvs.example.comonly) - Explicitly out-of-scope assets (M&A subsidiaries, legacy products, third-party services)
- Reward eligibility for recon-only findings like subdomain takeovers
- Rate limit and testing restrictions
Once scope is clear, start passive information gathering β anything that doesn't send traffic directly to the target's infrastructure.
WHOIS and historical data:
whois example.comwhois example.comCertificate transparency logs (still one of the highest-yield subdomain sources):
curl -s "https://crt.sh/?q=%25.example.com&output=json" | jq -r '.[].name_value' | sort -ucurl -s "https://crt.sh/?q=%25.example.com&output=json" | jq -r '.[].name_value' | sort -uWayback Machine URLs for historical endpoints and parameters:
waybackurls example.com > wayback.txtwaybackurls example.com > wayback.txtGitHub and GitLab dorking for leaked credentials, internal URLs, or config files:
site:github.com "example.com" api_key
site:github.com "example.com" secretsite:github.com "example.com" api_key
site:github.com "example.com" secretStage 2: Subdomain Enumeration
This is where most of your attack surface gets discovered. Use multiple tools β no single subdomain enumerator finds everything.
Subfinder (passive, fast, good default):
subfinder -d example.com -all -o subs_subfinder.txtsubfinder -d example.com -all -o subs_subfinder.txtAmass (passive + active modes, slower but thorough):
amass enum -passive -d example.com -o subs_amass.txtamass enum -passive -d example.com -o subs_amass.txtAssetfinder:
assetfinder --subs-only example.com > subs_assetfinder.txtassetfinder --subs-only example.com > subs_assetfinder.txtMerge and dedupe everything:
cat subs_*.txt | sort -u > all_subs.txtcat subs_*.txt | sort -u > all_subs.txtBrute-force with a resolver for subdomains that don't show up passively:
puredns bruteforce wordlist.txt example.com -r resolvers.txt -w subs_brute.txtpuredns bruteforce wordlist.txt example.com -r resolvers.txt -w subs_brute.txtDon't stop at first-level subdomains. Run permutation tools like altdns or gotator against your known list to generate likely variations (dev-api, staging-app, api-v2) and re-resolve them.
Stage 3: Filtering for Live Hosts
A raw subdomain list is mostly noise. Filter it down to what's actually responding.
Probe for live HTTP/HTTPS hosts with httpx:
cat all_subs.txt | httpx -silent -status-code -title -tech-detect -o live_hosts.txtcat all_subs.txt | httpx -silent -status-code -title -tech-detect -o live_hosts.txtThis single command tells you which hosts are alive, what status code they return, their page title, and often the tech stack (nginx, WordPress, React, etc.) β all useful for prioritizing targets.
Sort by status code and technology. A 200 OK on an admin subdomain running an outdated CMS is worth investigating before a 403 on a well-hardened API gateway.
Stage 4: Port and Service Discovery
For assets where broader infrastructure is in scope, port scanning fills in what subdomain enumeration misses.
Naabu for fast port discovery across your whole live-host list:
naabu -list live_hosts.txt -top-ports 1000 -o open_ports.txtnaabu -list live_hosts.txt -top-ports 1000 -o open_ports.txtNmap for deeper service and version detection on interesting hosts:
nmap -sV -sC -p- target.example.com -oN nmap_target.txtnmap -sV -sC -p- target.example.com -oN nmap_target.txtLook specifically for exposed dev/admin services: Jenkins, Elasticsearch, Kibana, Grafana, phpMyAdmin, and internal tools accidentally left internet-facing.
Stage 5: Content and Directory Discovery
Once you know which hosts are alive, find out what's actually on them.
Directory and file brute-forcing with ffuf:
ffuf -u https://target.example.com/FUZZ -w /path/to/wordlist.txt -mc 200,301,302,403 -o ffuf_results.jsonffuf -u https://target.example.com/FUZZ -w /path/to/wordlist.txt -mc 200,301,302,403 -o ffuf_results.jsonUse a solid wordlist β SecLists' raft-large-directories.txt and raft-large-files.txt are strong defaults. Add technology-specific wordlists when you know the stack (e.g., WordPress plugin paths, Laravel debug routes).
Look specifically for:
/robots.txtand/sitemap.xmlβ often leak internal paths/.git/β exposed git directories are a goldmine/.env,/config.php,/backup.zipβ common misconfiguration leftovers/api/,/swagger.json,/graphqlβ API surface and documentation/.well-known/security.txtβ sometimes reveals internal contact structure or program details
Crawl for internal links and JS files:
katana -u https://target.example.com -jc -o katana_results.txtkatana -u https://target.example.com -jc -o katana_results.txtStage 6: JavaScript File Analysis
Modern web apps ship huge amounts of logic β and secrets β client-side. This step alone routinely turns up API keys, internal endpoint names, and forgotten functionality.
Pull all JS files from crawled URLs:
cat katana_results.txt | grep "\.js$" > js_files.txtcat katana_results.txt | grep "\.js$" > js_files.txtExtract endpoints and secrets with a tool like LinkFinder or SecretFinder:
python3 linkfinder.py -i js_files.txt -o cli
python3 secretfinder.py -i js_files.txt -o clipython3 linkfinder.py -i js_files.txt -o cli
python3 secretfinder.py -i js_files.txt -o cliManually skim minified bundles for:
- Hardcoded API keys or tokens
- Internal endpoint paths not referenced anywhere else
- Feature flags or debug parameters
- Comments left in by developers (yes, still common)
Stage 7: Parameter Discovery
Hidden parameters are where a lot of IDOR, SSRF, and injection bugs live, because they're rarely covered by the app's normal UI flow.
Combine your Wayback URLs and crawled URLs, then extract parameters:
cat wayback.txt katana_results.txt | grep '=' | sort -u > urls_with_params.txtcat wayback.txt katana_results.txt | grep '=' | sort -u > urls_with_params.txtUse Arjun or x8 to brute-force hidden parameters on key endpoints:
arjun -u https://target.example.com/api/endpoint -o arjun_results.jsonarjun -u https://target.example.com/api/endpoint -o arjun_results.jsonPrioritize parameters with names like id, redirect, url, token, role, debug, or admin β these tend to map directly to common bug classes.
Stage 8: Subdomain Takeover Checks
A quick, high-signal check that's easy to automate across your whole subdomain list.
subjack -w all_subs.txt -t 100 -timeout 30 -o takeover_results.txt -sslsubjack -w all_subs.txt -t 100 -timeout 30 -o takeover_results.txt -sslLook for CNAME records pointing to unclaimed services (S3 buckets, GitHub Pages, Heroku, Azure, etc.). These are often straightforward, well-documented findings that programs pay out reliably.
Putting It Together: A Repeatable Loop
Recon isn't a one-time pass. The best hunters treat it as a loop they re-run periodically against the same target:
- Re-enumerate subdomains weekly or monthly β new assets appear constantly
- Diff your results against previous runs to spot what's new
- Re-check JS files after app updates β new deploys often reintroduce old leaks
- Automate as much of this as possible with a scheduled pipeline (cron job, GitHub Actions, or a dedicated recon server)
The hunters who consistently place on leaderboards aren't necessarily better at exploitation they just see more of the attack surface than everyone else, because they never stopped mapping it.
Final Notes
Recon output is only as good as what you do with it. Once you have live hosts, endpoints, and parameters mapped, cross-reference against known CVEs for the detected tech stack, check for default credentials on exposed admin panels, and always validate findings manually before reporting automated tools produce false positives constantly.
Stay within scope, respect rate limits, and document everything as you go. A clean recon trail makes writing a solid report and getting paid for it a lot easier.