"Recon is not just a phase — it's a mindset. And automation is what turns that mindset into a system."
1. Understanding the Target: Scope Analysis First, Always
Early in my bug bounty journey, I spent 6 hours fuzzing an endpoint only to realize it was explicitly out of scope. Six hours. Gone. That pain taught me: read the brief before you touch a single tool.
Reading the Program Like a Contract
Every bug bounty program on HackerOne, Bugcrowd, Intigriti, or YesWeHack has a brief that tells you exactly what you can and cannot touch. Read it like a legal document — because in a way, it is.
Pay special attention to:
- In-scope assets: Specific domains, IP ranges, mobile apps, APIs
- Out-of-scope assets: Often includes third-party services, CDN endpoints, acquisitions
- Vulnerability categories: Some programs exclude DoS, social engineering, or rate limiting
- Reward tiers: Critical, High, Medium, Low — know what pays
Private programs are gold. Less competition, faster triage, higher payouts. If you're invited to one, prioritize it. Public programs on Bugcrowd or HackerOne are great for learning but expect slow responses and duplicates.
Automation Tip: Save Your Scope
Save your in-scope domains to a file from the very beginning:
# scope.txt
target.com
*.target.com
api.target.com
admin.target.comEvery tool in your pipeline will reference this file. Don't hardcode domains — parameterize everything.
💡 Pro Tip: Use
inscopetool from tomnomnom to filter your recon output automatically against your scope file.cat all_subs.txt | inscope -scope scope.txt
Takeaway: Know the rules before you run a single command.
2. Subdomain Enumeration: The Foundation of Everything
I once found a critical vulnerability on staging2.internal.target.com — a subdomain that appeared in exactly zero Google results, zero certificate logs, and zero passive sources. It only showed up through permutation scanning.
Permutations matter.
Phase 1: Passive Recon (Don't Touch the Target)
Passive recon means gathering data without sending a single packet to the target. It's stealthy, fast, and often underused.
# Subfinder — queries 50+ passive sources
subfinder -d target.com -o passive_subs.txt -all -recursive
# Amass — the heavy artillery of passive recon
amass enum -passive -d target.com -o amass_passive.txt# crt.sh — certificate transparency logs (goldmine)
curl -s "https://crt.sh/?q=%.target.com&output=json" | \
jq -r '.[].name_value' | \
sed 's/\*\.//g' | \
sort -u >> passive_subs.txt# Combine and deduplicate
cat passive_subs.txt amass_passive.txt | sort -u > all_passive.txtPhase 2: Active Recon (Send Packets, Find More)
# DNSx — resolve all subdomains, filter live ones
cat all_passive.txt | dnsx -resp -a -cname -o live_subs.txt
# Gobuster DNS brute force
gobuster dns -d target.com \
-w /usr/share/seclists/Discovery/DNS/dns-Jhaddix.txt \
-t 50 \
-o gobuster_dns.txt# FFUF for virtual host discovery
ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
-u https://FUZZ.target.com \
-mc 200,301,302,403 \
-o ffuf_vhost.txtPhase 3: Permutation Scanning (The Hidden Gold)
Permutations generate subdomains that humans name but tools miss:
# Altdns — generates permutations from discovered subdomains
altdns -i live_subs.txt \
-w /opt/altdns/words.txt \
-o permutations_output.txt \
-r -s permuted_resolved.txt
# Gotator — advanced permutation engine
gotator -sub live_subs.txt \
-perm /opt/gotator/permutations.txt \
-depth 2 \
-numbers 3 \
-md \
| dnsx -silent -o permuted_live.txtThe Automated Subdomain Pipeline
#!/bin/bash
# sub_enum.sh — Full automated subdomain pipeline
DOMAIN=$1
OUTPUT_DIR="recon/$DOMAIN"
mkdir -p $OUTPUT_DIRecho "[*] Starting passive enumeration for $DOMAIN"
subfinder -d $DOMAIN -all -recursive -silent -o $OUTPUT_DIR/subfinder.txt
amass enum -passive -d $DOMAIN -o $OUTPUT_DIR/amass.txt
curl -s "https://crt.sh/?q=%.$DOMAIN&output=json" | \
jq -r '.[].name_value' | sed 's/\*\.//g' | \
sort -u > $OUTPUT_DIR/crtsh.txtecho "[*] Combining results"
cat $OUTPUT_DIR/*.txt | sort -u > $OUTPUT_DIR/all_subs.txtecho "[*] Resolving live subdomains"
cat $OUTPUT_DIR/all_subs.txt | dnsx -resp -silent -o $OUTPUT_DIR/live_subs.txtecho "[*] Running permutations"
altdns -i $OUTPUT_DIR/live_subs.txt \
-w ~/tools/altdns/words.txt \
-o $OUTPUT_DIR/permuted.txt \
-r -s $OUTPUT_DIR/permuted_live.txtecho "[+] Done. Total live subdomains: $(wc -l < $OUTPUT_DIR/live_subs.txt)"💡 Pro Tip: Schedule this script as a cron job to run every 24 hours against your target. New subdomains appear all the time — automate the monitoring, not just the discovery.
Takeaway: Combine passive, active, and permutation scanning to find what others miss.
3. Asset Discovery & Fingerprinting: Know What You're Attacking
A hunter in a Discord I frequent found a Grafana dashboard exposed on port 3000 of a forgotten subdomain. Default credentials. Admin access. Critical severity. He only found it because he ran a port scan across all live hosts — not just port 80/443.
HTTP Probing and Tech Fingerprinting
# Httpx — probe all live subs for HTTP/HTTPS
cat live_subs.txt | httpx \
-title \
-tech-detect \
-status-code \
-content-length \
-follow-redirects \
-o httpx_results.txt
# Extract just the URLs for further processing
cat httpx_results.txt | grep "200\|301\|302\|403" | \
awk '{print $1}' > live_urls.txtPort Scanning at Scale
# Naabu — fast port scanner (much faster than nmap at scale)
cat live_subs.txt | naabu \
-p 80,443,8080,8443,8888,3000,4000,5000,6379,9200,27017 \
-o open_ports.txt \
-silent
# Full nmap on interesting ports found
nmap -sV -sC -p $(cat open_ports.txt | grep "target.com" | \
awk -F: '{print $2}' | tr '\n' ',') \
-iL live_subs.txt \
-oA nmap_resultsScreenshot Automation: See Everything at Once
# Gowitness — screenshots all live URLs
gowitness file -f live_urls.txt \
--delay 2 \
--threads 10
# Generate a report you can browse
gowitness report generate# Eyewitness alternative (generates HTML report automatically)
eyewitness --web -f live_urls.txt \
--timeout 10 \
--threads 10Shodan/Censys for Exposed Assets
# Shodan CLI
shodan search "org:\"Target Company\"" --fields ip_str,port,hostnames \
> shodan_results.txt
# Shodan for specific tech stacks
shodan search "org:\"Target Company\" http.title:\"Jenkins\"" \
--fields ip_str,port# Censys via Python (more reliable API)
python3 -c "
import censys.certificates
c = censys.certificates.CensysCertificates()
for cert in c.search('parsed.names: target.com'):
print(cert.get('parsed.subject_dn', ''))
"💡 Pro Tip: Pipe all your httpx output through
grep -i "jenkins\|grafana\|kibana\|elastic\|admin\|dashboard"to immediately flag high-value targets that deserve manual attention.
Takeaway: Port scanning and screenshots reveal attack surface that HTTP-only recon completely misses.
4. JavaScript & Endpoint Mining: Where Secrets Hide
I once found AWS access keys sitting plainly in a minified JavaScript file loaded on the target's marketing page. The file had been there for 14 months — and the keys were still active.
It took just 20 minutes of JS analysis to uncover.
Automated JS File Discovery
# GAU — fetches known URLs from multiple archives
gau target.com | grep "\.js" | sort -u > jsfiles.txt
# Waybackurls — specifically from Wayback Machine
waybackurls target.com | grep "\.js$" >> jsfiles.txt# Katana — crawls live targets and extracts JS
katana -u https://target.com \
-jc \
-d 3 \
-o katana_js.txt# Combine
cat jsfiles.txt katana_js.txt | sort -u > all_js.txtExtracting Endpoints and Secrets
# LinkFinder — extracts endpoints from JS files
cat all_js.txt | while read url; do
python3 linkfinder.py -i $url -o cli 2>/dev/null
done | sort -u > js_endpoints.txt
# SecretFinder — hunts for API keys, tokens, secrets
cat all_js.txt | while read url; do
python3 SecretFinder.py -i $url -o cli 2>/dev/null
done | tee js_secrets.txt# Grep for common secret patterns yourself
cat downloaded_js/*.js | grep -Ei \
"(api_key|apikey|secret|token|password|aws_access|private_key|auth)" \
| grep -v "//.*api_key" \
> potential_secrets.txtFull JS Automation Script
#!/bin/bash
# js_recon.sh — full automated JS mining
DOMAIN=$1
DIR="recon/$DOMAIN/js"
mkdir -p $DIRecho "[*] Collecting JS URLs"
gau $DOMAIN | grep "\.js$" | sort -u > $DIR/js_urls.txt
katana -u https://$DOMAIN -jc -d 3 -silent | \
grep "\.js$" >> $DIR/js_urls.txt
cat $DIR/js_urls.txt | sort -u -o $DIR/js_urls.txtecho "[*] Downloading JS files"
mkdir -p $DIR/files
cat $DIR/js_urls.txt | while read url; do
filename=$(echo $url | md5sum | cut -d' ' -f1)
curl -sk "$url" -o "$DIR/files/$filename.js" 2>/dev/null
doneecho "[*] Extracting endpoints"
for f in $DIR/files/*.js; do
python3 ~/tools/LinkFinder/linkfinder.py -i $f -o cli 2>/dev/null
done | sort -u > $DIR/endpoints.txtecho "[*] Hunting for secrets"
for f in $DIR/files/*.js; do
python3 ~/tools/SecretFinder/SecretFinder.py -i $f -o cli 2>/dev/null
done | tee $DIR/secrets.txtecho "[+] JS recon done."
echo " Endpoints found: $(wc -l < $DIR/endpoints.txt)"
echo " Potential secrets: $(wc -l < $DIR/secrets.txt)"💡 Pro Tip: Always check endpoints found in JS against the live application. Internal API routes (
/api/v1/admin/users) often have IDOR or auth bypass issues that are never documented.
Takeaway: JavaScript files are the most underrated source of bugs. Automate their collection and analysis every single time.
5. Parameters & URL Discovery: The Input Attack Surface
A redirect parameter hidden in a 3-year-old Wayback URL — ?redirect_uri= — that was no longer visible on the live site but still functional. Open Redirect. Chained with OAuth. Account takeover. $2,000.
Historical URL Mining
# GAU for all historical URLs
gau target.com \
--threads 5 \
--retries 2 \
--blacklist png,jpg,gif,svg,css,woff \
| tee gau_urls.txt
# Waybackurls for additional coverage
waybackurls target.com >> gau_urls.txt# Deduplicate
cat gau_urls.txt | sort -u > all_urls.txt# Extract only URLs with parameters
cat all_urls.txt | grep "?" > parameterized_urls.txtParameter Mining and Fuzzing
# Paramspider — mines parameters from web archives
python3 paramspider.py --domain target.com \
--output paramspider_output.txt
# ARJUN — discovers hidden parameters through brute force
arjun -u https://target.com/api/user \
-m GET \
-oT arjun_get.txtarjun -u https://target.com/api/user \
-m POST \
-oT arjun_post.txt# x8 — alternative hidden parameter discoverer (very fast)
x8 -u "https://target.com/search?FUZZ=test" \
-w ~/wordlists/parameters.txt \
-o x8_results.txtFiltering for Juicy Parameters
# High-value parameters to focus on first
cat parameterized_urls.txt | grep -E \
"(id=|user=|uid=|redirect=|url=|next=|file=|path=|\
include=|load=|fetch=|read=|page=|callback=|return=|\
dest=|destination=|redir=|return_url=|api_key=|token=)" \
| sort -u > juicy_params.txt
# Normalize parameters for deduplication
cat juicy_params.txt | uro > unique_params.txt💡 Pro Tip: Use
qsreplacecombined with a SSRF payload to batch-test all redirect/URL parameters:cat juicy_params.txt | grep "redirect=" | qsreplace "https://your-collaborator.com" | xargs -I % curl -sk "%" -o /dev/null -w "%{redirect_url}\n"
Takeaway: Old URLs carry old vulnerabilities. Mine history aggressively.
6. GitHub Recon: The Hidden Goldmine
An employee at a target company pushed a config file to a personal GitHub repo 18 months before I found it. It contained the company's internal API endpoint, credentials for a staging database, and an AWS key. All still active. This is why GitHub recon is non-negotiable.
Manual GitHub Dorking
Use GitHub's search directly with these patterns:
org:targetname "api_key"
org:targetname "password"
org:targetname "secret_key"
org:targetname "internal"
org:targetname ".env"
org:targetname "BEGIN RSA PRIVATE KEY"
org:targetname "aws_access_key_id"
org:targetname "mongodb+srv"
org:targetname "DB_PASSWORD"
org:targetname filename:.env
org:targetname filename:config.yml password
org:targetname extension:pem privateAutomated GitHub Recon
# Gitdorker — automates GitHub dorking
python3 gitdorker.py \
-tf ~/tools/gitdorker/tokens.txt \
-q targetname \
-d ~/tools/gitdorker/dorks/medium_dorks.txt \
-o gitdorker_results.txt
# TruffleHog — deep scans repos for secrets
trufflehog github \
--org=targetname \
--token=$GITHUB_TOKEN \
--json \
| tee trufflehog_results.json# GitLeaks — scan a specific repo
gitleaks detect \
--source /path/to/cloned/repo \
--report-format json \
--report-path gitleaks_results.json# Scan all repos of an org
#!/bin/bash
ORG="targetname"
TOKEN="your_github_token"# Fetch all repos
curl -s -H "Authorization: token $TOKEN" \
"https://api.github.com/orgs/$ORG/repos?per_page=100" \
| jq -r '.[].clone_url' > org_repos.txt# Clone and scan each
while read repo; do
dir=$(basename $repo .git)
git clone --quiet $repo /tmp/$dir
gitleaks detect --source /tmp/$dir \
--report-format json \
--report-path "leaks_$dir.json" 2>/dev/null
rm -rf /tmp/$dir
done < org_repos.txt💡 Pro Tip: Don't only search the org's official repos — also search for the company name in personal repos. Employees often push work to personal accounts by mistake. Search:
"target.com" in:code password
Takeaway: GitHub is the most overlooked external attack surface. One leaked key can lead to full account takeover.
7. Google Dorking: Passive Intelligence at Scale
site:target.com filetype:log — I found a server log file publicly accessible that contained session tokens, internal IP addresses, and error stack traces. Three separate bugs from one dork.
The Essential Dork List
# Exposed files and configs
site:target.com ext:log
site:target.com ext:env
site:target.com ext:xml inurl:config
site:target.com filetype:sql
site:target.com filetype:bak
# Admin and login panels
site:target.com inurl:admin
site:target.com inurl:login
site:target.com inurl:dashboard
site:target.com inurl:portal# API and developer assets
site:target.com "api_key"
site:target.com inurl:api/v1
site:target.com inurl:swagger
site:target.com inurl:graphql# Exposed sensitive directories
site:target.com intitle:"index of"
site:target.com intitle:"index of" "parent directory"
site:target.com inurl:/.git/# Error messages and debug info
site:target.com "SQL syntax"
site:target.com "Warning: mysql"
site:target.com "stack trace"
site:target.com intext:"Internal Server Error"Automate Dorking with Python
#!/usr/bin/env python3
# google_dork.py — Automate Google Dorking
import time
from googlesearch import searchTARGET = "target.com"DORKS = [
f"site:{TARGET} ext:log",
f"site:{TARGET} filetype:env",
f"site:{TARGET} inurl:admin",
f"site:{TARGET} inurl:swagger",
f"site:{TARGET} \"api_key\"",
f"site:{TARGET} intitle:\"index of\"",
f"site:{TARGET} inurl:/.git/",
f"site:{TARGET} inurl:api/v1",
]results = []
for dork in DORKS:
print(f"[*] Running: {dork}")
try:
for url in search(dork, num_results=10, sleep_interval=3):
results.append({"dork": dork, "url": url})
print(f" [+] {url}")
except Exception as e:
print(f" [-] Error: {e}")
time.sleep(5) # Be respectful, avoid rate limitingwith open("dork_results.txt", "w") as f:
for r in results:
f.write(f"{r['dork']} | {r['url']}\n")💡 Pro Tip: Use
site:target.comcombined with LinkedIn dorks to find employee names and internal project codenames:site:linkedin.com "target.com" "engineer"— these codenames often appear as subdomain names or GitHub repo names.
Takeaway: Google has already indexed your target's mistakes. Let it tell you where they are.
8. Cloud & S3 Bucket Recon
target-dev-assets.s3.amazonaws.com — listable. Inside: database backups, internal documents, and a credentials.json file. The bucket name was guessed from the company name plus common suffixes. It took 3 minutes to find.
Automated S3 Bucket Discovery
# S3Scanner — scans for open/misconfigured buckets
s3scanner scan \
--bucket target-company \
--threads 100
# CloudEnum — multi-cloud asset enumeration
python3 cloud_enum.py \
-k target \
-k targetcompany \
-k target-company \
--disable-azure \
--disable-gcp# Generate permutations of bucket names
python3 -c "
company = 'target'
suffixes = ['dev', 'prod', 'staging', 'backup', 'data',
'assets', 'files', 'internal', 'logs', 'temp',
'test', 'media', 'uploads', 'static', 'public']
prefixes = ['', 'aws-', 's3-', 'cdn-']
for p in prefixes:
for s in suffixes:
print(f'{p}{company}-{s}')
print(f'{p}{company}.{s}')
print(f'{p}{s}-{company}')
" > bucket_wordlist.txt# Scan all permutations
cat bucket_wordlist.txt | while read bucket; do
aws s3 ls s3://$bucket 2>/dev/null && echo "[OPEN] $bucket"
doneGrayhatWarfare — Search Already-Found Buckets
# Use GrayhatWarfare API for indexed open buckets
curl -s "https://buckets.grayhatwarfare.com/api/v1/buckets?keywords=target&access_key=YOUR_API_KEY" \
| jq -r '.buckets[].bucket'💡 Pro Tip: Don't just check AWS S3. Also check Google Cloud Storage (
storage.googleapis.com), Azure Blob Storage (.blob.core.windows.net), and DigitalOcean Spaces. CloudEnum handles all of these in one run.
Takeaway: Cloud misconfigurations are the lowest-effort, highest-impact bugs in bug bounty.
9. Recon Automation: Building Your Full Pipeline
This is the section most guides skip. Let's build the actual machine.
Instead of rushing blindly, I relied on my automated recon pipeline. Within 22 minutes, it had already mapped out the target surface and sent me a Telegram alert with a clean, prioritized list of potential entry points.
The Full Automated Recon Pipeline
#!/bin/bash
# full_recon.sh — The Complete Automated Recon Pipeline
# Usage: ./full_recon.sh target.com "Your Telegram Bot Token" "Your Chat ID"
DOMAIN=$1
BOT_TOKEN=$2
CHAT_ID=$3
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
DIR="recon/${DOMAIN}_${TIMESTAMP}"
NOTIFY_CMD="notify -silent -bulk -provider telegram -telegram-id $CHAT_ID"mkdir -p $DIR/{subs,http,ports,js,params,github,screenshots}notify_telegram() {
curl -s -X POST "https://api.telegram.org/bot$BOT_TOKEN/sendMessage" \
-d "chat_id=$CHAT_ID" \
-d "text=$1" \
-d "parse_mode=Markdown" > /dev/null
}notify_telegram "🚀 *Recon started* for \`$DOMAIN\` at $(date)"# ============================================================
# PHASE 1: SUBDOMAIN ENUMERATION
# ============================================================
echo "[*] Phase 1: Subdomain Enumeration"
notify_telegram "📡 *Phase 1:* Subdomain enumeration running..."subfinder -d $DOMAIN -all -recursive -silent -o $DIR/subs/subfinder.txt
amass enum -passive -d $DOMAIN -o $DIR/subs/amass.txt 2>/dev/null
curl -s "https://crt.sh/?q=%.$DOMAIN&output=json" | \
jq -r '.[].name_value' | sed 's/\*\.//g' | \
sort -u > $DIR/subs/crtsh.txtcat $DIR/subs/*.txt | sort -u > $DIR/subs/all_subs.txt
TOTAL_SUBS=$(wc -l < $DIR/subs/all_subs.txt)# Resolve live subdomains
cat $DIR/subs/all_subs.txt | dnsx -silent -resp \
-o $DIR/subs/live_subs.txt
LIVE_SUBS=$(wc -l < $DIR/subs/live_subs.txt)notify_telegram "✅ *Subdomains:* $TOTAL_SUBS found, $LIVE_SUBS live"# ============================================================
# PHASE 2: HTTP PROBING + SCREENSHOTS
# ============================================================
echo "[*] Phase 2: HTTP Probing"
notify_telegram "🌐 *Phase 2:* HTTP probing and screenshots..."cat $DIR/subs/live_subs.txt | httpx \
-title -tech-detect -status-code -content-length \
-follow-redirects -silent \
-o $DIR/http/httpx_results.txtcat $DIR/http/httpx_results.txt | awk '{print $1}' \
> $DIR/http/live_urls.txt# Screenshot all live URLs
gowitness file -f $DIR/http/live_urls.txt \
--delay 2 --threads 10 \
--screenshot-path $DIR/screenshots/ \
2>/dev/nullLIVE_HTTP=$(wc -l < $DIR/http/live_urls.txt)
notify_telegram "✅ *HTTP:* $LIVE_HTTP live HTTP services found"# ============================================================
# PHASE 3: PORT SCANNING
# ============================================================
echo "[*] Phase 3: Port Scanning"
notify_telegram "🔍 *Phase 3:* Port scanning interesting ports..."cat $DIR/subs/live_subs.txt | naabu \
-p 21,22,80,443,2375,3000,4000,4848,5000,5900,\
6379,8080,8443,8888,9000,9200,9300,27017,28017 \
-silent \
-o $DIR/ports/open_ports.txtOPEN_PORTS=$(wc -l < $DIR/ports/open_ports.txt)
notify_telegram "✅ *Ports:* $OPEN_PORTS open ports found"# Flag interesting ports immediately
cat $DIR/ports/open_ports.txt | grep \
-E ":3000|:4848|:6379|:9200|:27017|:2375" \
| tee $DIR/ports/interesting_ports.txtif [ -s $DIR/ports/interesting_ports.txt ]; then
notify_telegram "🚨 *HIGH INTEREST PORTS:*\n$(cat $DIR/ports/interesting_ports.txt | head -10)"
fi# ============================================================
# PHASE 4: JS MINING
# ============================================================
echo "[*] Phase 4: JavaScript Mining"
notify_telegram "📄 *Phase 4:* JavaScript endpoint and secret hunting..."gau $DOMAIN --threads 5 --blacklist png,jpg,gif,svg,css,woff \
| grep "\.js$" | sort -u > $DIR/js/js_urls.txtkatana -u https://$DOMAIN -jc -d 3 -silent \
| grep "\.js$" >> $DIR/js/js_urls.txtcat $DIR/js/js_urls.txt | sort -u -o $DIR/js/js_urls.txtmkdir -p $DIR/js/files
while read url; do
fn=$(echo $url | md5sum | cut -d' ' -f1)
curl -sk "$url" -o "$DIR/js/files/$fn.js" 2>/dev/null
done < $DIR/js/js_urls.txt# Extract secrets
for f in $DIR/js/files/*.js; do
python3 ~/tools/SecretFinder/SecretFinder.py \
-i $f -o cli 2>/dev/null
done | grep -v "No result" | sort -u > $DIR/js/secrets.txtJS_COUNT=$(wc -l < $DIR/js/js_urls.txt)
SECRET_COUNT=$(wc -l < $DIR/js/secrets.txt)
notify_telegram "✅ *JS:* $JS_COUNT files analyzed, $SECRET_COUNT potential secrets"if [ -s $DIR/js/secrets.txt ]; then
notify_telegram "🔑 *POTENTIAL SECRETS FOUND:*\n$(cat $DIR/js/secrets.txt | head -5)"
fi# ============================================================
# PHASE 5: PARAMETER DISCOVERY
# ============================================================
echo "[*] Phase 5: Parameter Discovery"
notify_telegram "🔗 *Phase 5:* URL and parameter harvesting..."gau $DOMAIN --threads 5 \
--blacklist png,jpg,gif,svg,css,woff,ttf,ico \
| tee $DIR/params/gau_urls.txt | grep "?" \
> $DIR/params/parameterized.txtcat $DIR/params/parameterized.txt | grep -E \
"(id=|user=|redirect=|url=|file=|path=|include=|token=)" \
| sort -u > $DIR/params/juicy_params.txtPARAM_COUNT=$(wc -l < $DIR/params/juicy_params.txt)
notify_telegram "✅ *Params:* $PARAM_COUNT high-value parameterized URLs"# ============================================================
# FINAL SUMMARY
# ============================================================
SUMMARY="
🎯 *Recon Complete for $DOMAIN*📊 *Summary:*
• Subdomains: $TOTAL_SUBS total / $LIVE_SUBS live
• HTTP Services: $LIVE_HTTP
• Open Ports: $OPEN_PORTS
• JS Files: $JS_COUNT
• Potential Secrets: $SECRET_COUNT
• Juicy Params: $PARAM_COUNT📁 Results saved to: $DIR
⏰ Completed at: $(date)
"notify_telegram "$SUMMARY"
echo "$SUMMARY"Setting Up Axiom for Distributed Recon
For large targets, run recon across multiple cloud instances:
# Initialize Axiom
axiom-configure
# Spin up 10 instances
axiom-fleet launch recon --count 10# Run subfinder across the fleet
axiom-scan scope.txt -m subfinder -o all_subs.txt# Run httpx across the fleet
axiom-scan all_subs.txt -m httpx -o httpx_results.txt# Naabu port scan across all instances
axiom-scan all_subs.txt -m naabu -o ports.txtTelegram Notification Bot Setup
#!/usr/bin/env python3
# notify_bot.py — Send recon alerts to Telegram
import requests
import sysBOT_TOKEN = "YOUR_BOT_TOKEN"
CHAT_ID = "YOUR_CHAT_ID"def send_telegram(message, parse_mode="Markdown"):
url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage"
payload = {
"chat_id": CHAT_ID,
"text": message,
"parse_mode": parse_mode
}
r = requests.post(url, json=payload)
return r.json()def send_file(filepath, caption=""):
url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendDocument"
with open(filepath, 'rb') as f:
files = {'document': f}
payload = {"chat_id": CHAT_ID, "caption": caption}
r = requests.post(url, files=files, data=payload)
return r.json()if __name__ == "__main__":
if len(sys.argv) > 1:
send_telegram(sys.argv[1])Monitoring New Subdomains Over Time
#!/bin/bash
# monitor_subs.sh — Run daily via cron, alert on new subdomains
DOMAIN=$1
PREV_FILE="recon/${DOMAIN}/live_subs_prev.txt"
CURR_FILE="recon/${DOMAIN}/live_subs_curr.txt"# Run enumeration
subfinder -d $DOMAIN -all -silent | \
dnsx -silent > $CURR_FILE# Diff against previous results
if [ -f $PREV_FILE ]; then
NEW=$(diff $PREV_FILE $CURR_FILE | grep "^>" | awk '{print $2}')
if [ -n "$NEW" ]; then
python3 notify_bot.py "🆕 *New subdomains on $DOMAIN:*\n$NEW"
fi
fi# Update previous
cp $CURR_FILE $PREV_FILEAdd to cron: 0 6 * * * /bin/bash /opt/recon/monitor_subs.sh target.com
💡 Pro Tip: Use
notifyby ProjectDiscovery to integrate with Slack, Discord, and Telegram simultaneously. A single| notifyat the end of any command sends output to all your configured channels.
Takeaway: Manual recon once is good. Automated recon running continuously is how you find bugs while you sleep.
10. Organizing Recon Data: From Chaos to Clarity
A friend collected 50,000 URLs across 3 months of recon and stored them in a single text file. When he finally wanted to look for SSRF candidates, it took him 4 hours just to filter the data. Organization is not optional — it's part of the methodology.
Storing Data in SQLite
#!/usr/bin/env python3
# recon_db.py — Store and query recon data
import sqlite3
import jsonconn = sqlite3.connect("recon.db")
c = conn.cursor()# Create tables
c.executescript("""
CREATE TABLE IF NOT EXISTS subdomains (
id INTEGER PRIMARY KEY,
domain TEXT NOT NULL,
subdomain TEXT UNIQUE,
status_code INTEGER,
title TEXT,
tech_stack TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);CREATE TABLE IF NOT EXISTS endpoints (
id INTEGER PRIMARY KEY,
subdomain_id INTEGER,
url TEXT UNIQUE,
method TEXT,
parameters TEXT,
source TEXT,
FOREIGN KEY (subdomain_id) REFERENCES subdomains(id)
);CREATE TABLE IF NOT EXISTS findings (
id INTEGER PRIMARY KEY,
title TEXT,
url TEXT,
severity TEXT,
description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
""")def insert_subdomain(domain, subdomain, status=None, title=None, tech=None):
c.execute("""
INSERT OR IGNORE INTO subdomains
(domain, subdomain, status_code, title, tech_stack)
VALUES (?, ?, ?, ?, ?)
""", (domain, subdomain, status, title, tech))
conn.commit()def get_interesting_subdomains(domain):
"""Find subdomains with non-standard ports or specific tech"""
c.execute("""
SELECT subdomain, title, tech_stack
FROM subdomains
WHERE domain=? AND (
tech_stack LIKE '%Jenkins%' OR
tech_stack LIKE '%Grafana%' OR
tech_stack LIKE '%Elasticsearch%' OR
title LIKE '%admin%' OR
title LIKE '%dashboard%'
)
""", (domain,))
return c.fetchall()Using Obsidian for Manual Notes
Create a structured vault with:
- One note per target domain
- Subpages for each interesting subdomain
- Tags:
#ssrf-candidate,#idor-candidate,#open-redirect,#reported - Embed screenshots directly into notes
What to Look For First (Priority Checklist)
HIGH PRIORITY (check immediately):
[ ] Login panels on non-standard ports
[ ] Dev/staging environments (dev., staging., test., qa.)
[ ] Admin panels (admin., backend., internal.)
[ ] APIs without visible documentation
[ ] Old/legacy subdomains (legacy., old., v1., v2.)
[ ] Exposed .git directories
[ ] Swagger/OpenAPI documentation endpoints
[ ] GraphQL endpoints (/graphql, /api/graphql)
MEDIUM PRIORITY:
[ ] JS files with API keys or internal endpoints
[ ] Error pages revealing stack traces
[ ] Parameters matching SSRF/redirect/SQLi patterns
[ ] S3 bucket names in page sourceLOWER PRIORITY (batch process):
[ ] All parameterized URLs for fuzzing
[ ] Historical endpoints from Wayback
[ ] Tech fingerprinting results for known CVEs💡 Pro Tip: Create a Burp Suite project for each target and use the "Target" tab's scope and sitemap to organize manual testing. Import your httpx results directly into Burp's sitemap for a head start.
Takeaway: Your recon data is only as valuable as your ability to query and act on it.
11. From Recon to Bug — Real Case Studies
Case Study 1: Forgotten Subdomain → SSRF
Recon: Automated subdomain monitoring pinged me about legacy-api.target.com — a new entry that appeared when the company migrated systems.
Discovery: httpx showed it running an old version of an internal service. No WAF. A parameter ?webhook_url= was found in a 3-year-old Wayback URL.
Exploitation: The parameter made server-side HTTP requests. Pointed to http://169.254.169.254/latest/meta-data/ — AWS metadata service responded with IAM credentials.
Lesson: Automated monitoring found a subdomain that appeared during a migration window. Without continuous monitoring, it would have been missed.
Case Study 2: JS File → Internal API → IDOR → $1,500
Recon: SecretFinder flagged an internal API base URL — https://internal-api.target.com/v3 — in a minified production JS file.
Discovery: The endpoint accepted GET /v3/users/{id}/profile. IDs were sequential integers. No authorization check between accounts.
Exploitation: Changing id=1234 to id=1235 returned another user's full profile including email, phone number, and address.
Lesson: JS files expose internal infrastructure that developers assume nobody will see. They're wrong.
Case Study 3: GitHub Dork → AWS Key
Recon: TruffleHog scanning the org's GitHub found an aws_access_key_id and aws_secret_access_key in a 9-month-old commit that was never removed from history.
Discovery: git log --all -p | grep -A 2 "aws_access_key_id" confirmed the keys were still in the history even though the file was "deleted."
Exploitation: The keys were active. Had full S3 read access to a bucket containing customer PII and internal database backups.
Lesson: Deleting a file from a Git repo does NOT remove it from history. Use git filter-branch or BFG Repo Cleaner to purge secrets properly.
12. Common Mistakes Bug Hunters Make in Recon
Rushing to attack without finishing recon. The urge to start fuzzing immediately is real — resist it. A complete recon map takes 2–4 hours minimum. That time pays off for every subsequent session on the same target.
Ignoring permutations. Most hunters run Subfinder and call it done. The bugs are often on subdomains that only permutation scanning reveals — dev2., api-new., legacy-internal.. These are forgotten, less-secured, and more vulnerable.
Not revisiting targets. Companies deploy new features, new subdomains, and new infrastructure constantly. A target you checked 3 months ago is a different attack surface today. Automate continuous monitoring and you'll always be first on new assets.
Skipping cloud asset recon. S3 buckets, GCP storage, Azure blobs — these are often not in scope documentation because the company forgot they existed. If the wildcard scope covers *.target.com, a misconfigured cloud asset under the same org is often fair game.
Treating recon as a one-time phase. The best bug hunters return to recon between every testing session. New JS files, new parameters, new subdomains — all of these emerge continuously. Recon is a loop, not a phase.
13. Pro Tools Summary Table
Passive Subs
Subfinder — Multi-source passive subdomain discovery
subfinder -d target.com -all -o subs.txt
Amass — Comprehensive OSINT enumeration
amass enum -passive -d target.com
crt.sh — Certificate transparency logs
curl "https://crt.sh/?q=%.target.com&output=json"
Active DNS
DNSx — Mass DNS resolution & validation
cat subs.txt | dnsx -resp -o live.txt
Gobuster — DNS brute force enumeration
gobuster dns -d target.com -w wordlist.txt
Permutations
Altdns — Subdomain permutation generation
altdns -i live.txt -w words.txt -r -s out.txt
Gotator — Advanced permutation engine
gotator -sub subs.txt -perm perms.txt | dnsx
HTTP Probing
Httpx — Fast HTTP probing with tech detection
cat subs.txt | httpx -title -tech-detect
Screenshots
Gowitness — Automated web screenshots
gowitness file -f urls.txt
Port Scanning
Naabu — Fast port discovery at scale
cat subs.txt | naabu -p common -o ports.txt
OSINT
Shodan — Internet-connected device search
shodan search "org:Company"
JS Mining
GAU — Historical URL fetching
gau target.com | grep ".js$"
Katana — Smart web crawler with JS parsing
katana -u https://target.com -jc
JS Analysis
LinkFinder — JS endpoint extraction
python3 linkfinder.py -i file.js -o cli
SecretFinder — API key and secret detection in JS
python3 SecretFinder.py -i file.js -o cli
URL Mining
Waybackurls — Wayback Machine URL extraction
waybackurls target.com
Params
Paramspider — Parameter mining from archives
paramspider --domain target.com
ARJUN — Hidden parameter fuzzing
arjun -u https://target.com/api -m GET
GitHub
TruffleHog — Secret scanning in Git history
trufflehog github --org=targetname
Gitdorker — Automated GitHub dorking
python3 gitdorker.py -q targetname
Cloud
S3Scanner — S3 bucket misconfiguration check
s3scanner scan --bucket target-company
CloudEnum — Multi-cloud asset discovery
python3 cloud_enum.py -k target
GrayhatWarfare — Search indexed open buckets Web UI + API
Automation
Axiom — Distributed cloud recon
axiom-scan subs.txt -m subfinder
Notifications
Notify — Multi-platform alert integration
cat results.txt | notify
Dedup
URO — Intelligent URL deduplication
cat urls.txt | uro
Filtering
Inscope — Filter results against scope
cat subs.txt | inscope -scope scope.txt

14. Conclusion: Consistency Beats Talent
Here's the truth about bug bounty that nobody wants to admit: the hunters earning consistent payouts aren't necessarily the most skilled hackers. They're the most systematic ones.
They run their pipeline on every target. They monitor continuously. They automate the boring parts so they can focus mental energy on the creative parts — actually understanding the application, thinking like a developer, finding the logical flaws that no automated scanner can detect.
The automation is not cheating. The automation is the work. Building a solid, reliable recon pipeline is a skill in itself — one that compounds over time as you add new tools, new dorks, and new techniques from every bug you find.
"The best recon is the recon you do consistently — not the perfect recon you run once."
Your action items starting today:
- Clone and install:
subfinder,amass,httpx,dnsx,naabu,gau,katana,gowitness - Create your recon folder structure and write your first pipeline script
- Set up a Telegram bot and wire it to
notify - Pick one bug bounty program and run the full pipeline — see what it finds
- Schedule
monitor_subs.shas a daily cron job on a cheap VPS
The pipeline above took me 18 months of iteration to build. You now have it in one article. The only thing left is to run it.
Good luck. Stay in scope. Report responsibly.
About the author: WolfSec is a bug hunter and a pentester focused on web application security. Writing about the techniques that actually work — not just the ones that look good in tutorials.
follow me @lazyhackerbd