| name | web2-recon |
| description | Web2 recon pipeline — subdomain enumeration (subfinder, Chaos API, assetfinder), live host discovery (dnsx, httpx), URL crawling (katana, waybackurls, gau), directory fuzzing (ffuf), JS analysis (LinkFinder, SecretFinder), continuous monitoring (new subdomain alerts, JS change detection, GitHub commit watch). Use when starting recon on any web2 target or when asked about asset discovery, subdomain enum, or attack surface mapping. |
WEB2 RECON PIPELINE
Full asset discovery from nothing to a prioritized URL list ready for hunting.
SETUP (one-time)
export CHAOS_API_KEY="your-key-here"
echo 'export CHAOS_API_KEY="your-key-here"' >> ~/.zshrc
nuclei -update-templates
mkdir -p ~/.config/subfinder
cat > ~/.config/subfinder/config.yaml << 'EOF'
virustotal: [YOUR_VT_KEY]
securitytrails: [YOUR_ST_KEY]
censys_apiid: YOUR_CENSYS_ID
censys_secret: YOUR_CENSYS_SECRET
shodan: [YOUR_SHODAN_KEY]
EOF
which subfinder httpx dnsx nuclei katana waybackurls gau dalfox ffuf anew gf interactsh-client
THE 5-MINUTE RULE
If a target shows nothing interesting after 5 minutes of recon, move on. Don't burn hours on dead surface.
5-minute kill signals:
- All subdomains return 403 or static marketing pages
- No API endpoints visible in URLs
- No JavaScript bundles with interesting endpoint paths
- nuclei returns 0 medium/high findings
- No forms, no authentication, no user data
STANDARD RECON PIPELINE
Pre-Hunt: Always Run First
TARGET="target.com"
curl -s "https://crt.sh/?q=%.${TARGET}&output=json" \
| jq -r '.[].name_value' \
| sed 's/\*\.//g' \
| sort -u > /tmp/subs.txt
echo "[+] crt.sh: $(wc -l < /tmp/subs.txt) subdomains"
curl -s "https://dns.projectdiscovery.io/dns/$TARGET/subdomains" \
-H "Authorization: $CHAOS_API_KEY" \
| jq -r '.[]' >> /tmp/subs.txt
echo "[+] Chaos returned $(wc -l < /tmp/subs.txt) subdomains"
subfinder -d $TARGET -silent | anew /tmp/subs.txt
assetfinder --subs-only $TARGET | anew /tmp/subs.txt
echo "[+] Total subdomains after all sources: $(wc -l < /tmp/subs.txt)"
cat /tmp/subs.txt | dnsx -silent | httpx -silent -status-code -title -tech-detect | tee /tmp/live.txt
echo "[+] Live hosts: $(wc -l < /tmp/live.txt)"
cat /tmp/live.txt | awk '{print $1}' | katana -d 3 -jc -kf all -silent | anew /tmp/urls.txt
echo $TARGET | waybackurls | anew /tmp/urls.txt
gau $TARGET --subs | anew /tmp/urls.txt
echo "[+] Total URLs: $(wc -l < /tmp/urls.txt)"
nuclei -l /tmp/live.txt -t ~/nuclei-templates/ -severity critical,high,medium -o /tmp/nuclei.txt
Output to Organized Directory
TARGET="target.com"
RECON_DIR="recon/$TARGET"
mkdir -p $RECON_DIR
/tmp/subs.txt → $RECON_DIR/subdomains.txt
/tmp/live.txt → $RECON_DIR/live-hosts.txt
/tmp/urls.txt → $RECON_DIR/urls.txt
/tmp/nuclei.txt → $RECON_DIR/nuclei.txt
ATTACK SURFACE TRIAGE
Find Interesting Targets in URL List
cat /tmp/urls.txt | grep -E "[?&](id|user|file|path|url|redirect|next|src|token|key|api_key)=" | tee /tmp/interesting-params.txt
cat /tmp/urls.txt | grep -E "/api/|/v1/|/v2/|/v3/|/graphql|/rest/|/gql" | tee /tmp/api-endpoints.txt
cat /tmp/urls.txt | grep -E "upload|file|attachment|document|image|avatar|photo|media" | tee /tmp/uploads.txt
cat /tmp/urls.txt | grep -E "/admin|/internal|/debug|/test|/staging|/dev|/management|/console" | tee /tmp/admin-paths.txt
cat /tmp/urls.txt | grep -E "/oauth|/login|/auth|/sso|/saml|/oidc|/callback|/token" | tee /tmp/auth-paths.txt
gf Patterns (Quick Classification)
cat /tmp/urls.txt | gf xss | tee /tmp/xss-candidates.txt
cat /tmp/urls.txt | gf ssrf | tee /tmp/ssrf-candidates.txt
cat /tmp/urls.txt | gf idor | tee /tmp/idor-candidates.txt
cat /tmp/urls.txt | gf sqli | tee /tmp/sqli-candidates.txt
cat /tmp/urls.txt | gf redirect | tee /tmp/redirect-candidates.txt
cat /tmp/urls.txt | gf lfi | tee /tmp/lfi-candidates.txt
cat /tmp/urls.txt | gf rce | tee /tmp/rce-candidates.txt
JS ANALYSIS
SecretFinder (API keys, tokens in JS bundles)
source ~/tools/SecretFinder/.venv/bin/activate
python3 ~/tools/SecretFinder/SecretFinder.py -i "https://target.com/static/js/main.js" -o cli
cat /tmp/urls.txt | grep "\.js$" | head -50 | while read url; do
echo "=== $url ==="
python3 ~/tools/SecretFinder/SecretFinder.py -i "$url" -o cli 2>/dev/null
done
deactivate
LinkFinder (Endpoints hidden in JS)
source ~/tools/LinkFinder/.venv/bin/activate
python3 ~/tools/LinkFinder/linkfinder.py -i "https://target.com/app.js" -o cli
python3 ~/tools/LinkFinder/linkfinder.py -i "https://target.com" -d -o cli
deactivate
DIRECTORY FUZZING
ffuf — Standard Fuzzing
ffuf -u "https://target.com/FUZZ" \
-w ~/wordlists/common.txt \
-mc 200,201,204,301,302,307,401,403 \
-ac \
-t 40 \
-o /tmp/ffuf-dirs.json
ffuf -u "https://target.com/api/FUZZ" \
-w ~/wordlists/api-endpoints.txt \
-mc 200,201,204,301,302 \
-ac \
-t 20
ffuf -request /tmp/req.txt \
-request-proto https \
-w <(seq 1 10000) \
-fc 404 \
-ac \
-t 10
TARGET SCORING — GO / NO-GO
Score before spending time. Skip if score < 4.
| Criterion | Points |
|---|
| Max bounty >= $5K | +2 |
| Large user base (>100K) or handles money | +2 |
| Program launched < 60 days ago | +2 |
| Complex features: API, OAuth, file upload, GraphQL | +1 |
| Recent code/feature changes (GitHub, changelog) | +1 |
| Private program (less competition) | +1 |
| Tech stack you know | +1 |
| Source code available | +1 |
| Prior disclosed reports to study | +1 |
< 4: Skip
4-5: Only if nothing better available
6-8: Good — spend 1-3 days
>= 9: Excellent — spend up to 1 week
Pre-Dive Hard Kill Signals
- Max bounty < $500 → not worth your time
- All recent reports are N/A or duplicate → hunters saturated it
- Scope is only a static marketing page → no attack surface
- Company < 5 employees with no revenue → won't pay
- Explicitly excludes your planned bug class in rules
TECH STACK DETECTION (2 min)
curl -sI https://target.com | grep -iE "server|x-powered-by|x-aspnet|x-runtime|x-generator"
Stack → Primary Bug Class Map
| Stack | Hunt First | Hunt Second |
|---|
| Ruby on Rails | Mass assignment | IDOR (:id routes) |
| Django | IDOR (ModelViewSet, no object perms) | SSTI (mark_safe) |
| Flask | SSTI (render_template_string) | SSRF (requests lib) |
| Laravel | Mass assignment ($fillable) | IDOR (Eloquent, no ownership) |
| Express (Node.js) | Prototype pollution | Path traversal |
| Spring Boot | Actuator endpoints (/actuator/env) | SSTI (Thymeleaf) |
| ASP.NET | ViewState deserialization | Open redirect (ReturnUrl) |
| Next.js | SSRF via Server Actions | Open redirect via redirect() |
| GraphQL | Introspection → auth bypass on mutations | IDOR via node(id:) |
| WordPress | Plugin SQLi | REST API auth bypass |
CONTINUOUS MONITORING SETUP
Set up once per target. Alerts you before other hunters.
New Subdomain Alerts (daily cron)
#!/bin/bash
TARGET="target.com"
KNOWN="/tmp/$TARGET-subs-known.txt"
subfinder -d $TARGET -silent > /tmp/$TARGET-subs-fresh.txt
curl -s "https://dns.projectdiscovery.io/dns/$TARGET/subdomains" \
-H "Authorization: $CHAOS_API_KEY" \
| jq -r '.[]' >> /tmp/$TARGET-subs-fresh.txt
NEW=$(comm -23 <(sort /tmp/$TARGET-subs-fresh.txt) <(sort $KNOWN 2>/dev/null))
if [ -n "$NEW" ]; then
echo "NEW SUBDOMAINS: $NEW"
echo "$NEW" >> $KNOWN
fi
GitHub Commit Watch
#!/bin/bash
REPO="TargetOrg/target-app"
LAST_SHA="/tmp/$REPO-last-sha.txt"
CURRENT=$(curl -s "https://api.github.com/repos/$REPO/commits?per_page=1" | jq -r '.[0].sha')
KNOWN=$(cat $LAST_SHA 2>/dev/null)
if [ "$CURRENT" != "$KNOWN" ]; then
echo "New commit on $REPO: $CURRENT"
echo $CURRENT > $LAST_SHA
curl -s "https://api.github.com/repos/$REPO/commits/$CURRENT" \
| jq -r '.files[].filename' | grep -E "auth|middleware|route|permission|role|admin"
fi
PORT SCANNING (often skipped — don't skip)
cat /tmp/live.txt | awk '{print $1}' | naabu -port 80,443,8080,8443,3000,4000,5000,8000,8888,9000,9090,9200,6379 -silent | tee /tmp/open-ports.txt
SECRET SCANNING IN JS BUNDLES
pip install trufflehog3 2>/dev/null || true
trufflehog filesystem --only-verified recon/$TARGET/ 2>/dev/null
source ~/tools/SecretFinder/.venv/bin/activate
cat /tmp/urls.txt | grep "\.js$" | head -100 | while read url; do
python3 ~/tools/SecretFinder/SecretFinder.py -i "$url" -o cli 2>/dev/null
done
deactivate
wget -q -r -l 1 -A "*.js" -P /tmp/js-files/ "https://$TARGET" 2>/dev/null
grep -rn "api_key\|apiKey\|client_secret\|access_token\|private_key\|AWS_SECRET\|AKIA" /tmp/js-files/ 2>/dev/null
GITHUB DORKING FOR TARGET
TARGET_ORG="TargetOrgName"
gh search code "api_key" --owner "$TARGET_ORG" --json path,repository 2>/dev/null | jq '.'
gh search code "password" --owner "$TARGET_ORG" --json path,repository 2>/dev/null | head -20
python3 ~/tools/GitDorker/GitDorker.py -t GITHUB_TOKEN -d ~/tools/GitDorker/Dorks/alldorksv3 -q "$TARGET" -org
30-MINUTE RECON PROTOCOL
Minutes 0-5: Read Program Page
Note:
- ALL in-scope assets (every domain listed)
- Out-of-scope list (read carefully — common trap)
- Safe harbor statement
- Impact types accepted (some exclude "low")
- Average bounty amount (signals program generosity)
Minutes 5-15: Asset Discovery
Run the standard pipeline above. Focus on live.txt output.
Minutes 15-25: Surface Map
Run gf patterns and the interesting-params grep above.
Minutes 25-30: Manual Exploration
Open Burp Suite. Browse the app with proxy on:
- Register an account
- Perform main user actions (create/read/update/delete resources)
- Note all API calls in Burp history
- Look for endpoints not in your URL list
After 30 min: Prioritize
Priority 1: API endpoints with ID parameters → IDOR candidates
Priority 2: File upload features → XSS/RCE candidates
Priority 3: OAuth/SSO flows → auth bypass candidates
Priority 4: Search/filter with user input → SQLi/SSRF/SSTI candidates
Priority 5: Admin/debug endpoints → auth bypass candidates