Stealth Web Recon — WAF-Aware Asset Discovery
Purpose
Map a target's external attack surface (subdomains, live web services, exposed panels, tech stack) WITHOUT getting blocked by Cloudflare, openresty rate limits, or other WAFs. The user explicitly requires blocks to be avoided — treat "don't trigger the WAF" as a hard constraint, not a nice-to-have.
This is a companion to the recon skill: use recon for the generic flow, this skill for the STEALTH discipline and command set that keeps you under WAF radar.
Core principle: passive first, active last, never burst
- Passive enumeration (crt.sh, subfinder, gau/Wayback) gathers hosts with zero requests to the target.
- DNS resolution (dnsx) is borderline-passive — uses public resolvers, but space the queries.
- Active HTTP probing (httpx) is the only step that "hits" the target — keep it slow and rate-limited.
- Content scanning (katana/ffuf/arjun) is skipped entirely on WAF/Bot-Management hosts.
Workflow (worked, zero-block sequence)
See references/stealth-recon.md for the exact command set and references/katana_pitfalls.md for katana crawl gotchas. Summary:
- Subdomain discovery (passive):
subfinder -d <target> -silent -recursive → then crt.sh JSON (external, may rate-limit — retry once or discard).
- DNS resolve:
dnsx -l subfinder.txt -silent -r 8.8.8.8,1.1.1.1.
- httpx probe (THE active step, rate-limited):
httpx -l resolved.txt -silent -t 30 -rate-limit 15 -timeout 8 -ports 80,443 -title -status-code -tech-detect -server -follow-redirects
-rate-limit 15 and -t 30 are mandatory. Never run httpx at default concurrency against a WAF host.
-tech-detect is what reveals Cloudflare Bot Management so you can exclude those hosts.
- Passive URL mining:
gau --subs <target> (Wayback, external — empty output is NORMAL, not a failure).
- Panel version confirmation: ONE slow
curl --max-time 15 -A "Mozilla/5.0" per host, sequential, sleep 1 between — never a burst.
WAF-exclusion rule (critical)
If httpx tech-detect shows Cloudflare + Cloudflare Bot Management, do header-only / passive checks on that host. Do NOT run katana/ffuf/arjun against it. Bot Management is the fastest path to a block. (Sensitive / Bot-Managed subdomains like admissions, enterprise assets, or core LMS should be excluded from aggressive content scanning.)
Abort / back-off signals
- HTTP
429 or a JS challenge page → STOP active scanning on that host, fall back to passive-only.
- External tools (crt.sh, gau) returning empty → non-critical, discard. Never compensate by hammering the target.
Crawl pitfalls (cost real wasted cycles — verified)
- katana
-silent discards ALL results. In v1.7.0, -silent routes crawl output to stderr; katana ... -silent > file 2>/dev/null yields a 0-byte file (exit 0, looks fine). Fix: use katana's own -o file (no -silent), OR drop -silent and capture stdout (> out.txt 2>err.txt). Full repro in references/katana_pitfalls.md.
- Headless katana empty.
-jc -kf all needs Chromium; absent → exit 0, 0 endpoints. Use standard HTTP crawl (-d 2 -rate-limit 8 -delay 1000) unless Chromium is confirmed present.
- Background katana SIGINT truncation. A background
katana ... > file whose parent shell gets SIGINT truncates output ("Ctrl+C pressed" in stderr). Wrap as setsid bash -c 'katana ... > file 2>err.txt; echo DONE >> file' so the child survives the parent signal group.
- Recover subdomains missed by subfinder from crawl links (Moodle/OJS/SaaS hosts referenced inside page HTML, e.g.
lms, research, journal).
Read-only default-credential validation (extends F-01 panel exposure)
When a panel is publicly reachable (HTTP 200 login), validate auth strength WITHOUT brute force:
- Max 3 common pairs per panel via its REAL login endpoint (form POST for Adminer/Proxmox/NPM; JSON
{"email"/"username","password"} for n8n/Kuma/DocuSeal).
- Inspect body, not just HTTP code (login failures often still return 200 with a re-rendered form). Markers: 401/400/"invalid"/"Invalid"/re-rendered login form = fail; a session/auth cookie or dashboard HTML = success.
- A
404 on the login path means the route is wrong (CSRF/session needed), NOT a success — report as inconclusive, do not keep guessing paths (that drifts into brute force).
- If default creds succeed → report Critical and STOP (no post-auth exploitation). If all fail → F-01 stays High (public exposure), not Critical.
- Never use enumerated usernames (e.g. from WP
/wp-json/wp/v2/users) to attack other login surfaces — that is spraying, out of scope.
- Exposed admin/automation panels without visible auth: Adminer, n8n, Uptime Kuma, Nginx Proxy Manager, DocuSeal, Proxmox, Portainer, Nessus.
nginx/1.14.0 (Ubuntu) default pages = server version leak + likely EOL.
- 502/500 on
*.dev-app.* and staging hosts = dead dev infrastructure exposed publicly.
- Missing security headers (X-Frame-Options, X-Content-Type-Options, CSP, Referrer-Policy) even when HSTS is present.
- WordPress version / readme.html leaks.
CVE version-matching (closes the audit loop)
After mapping versions, check them against known CVEs WITHOUT false positives — NVD 2.0 parsing + CPE affected-range check + sibling-plugin filtering. This is what turns "we saw a plugin version" into a credible (or cleared) finding. Full technique: references/cve-version-matching.md. Key traps: WPScan now needs a token; a CVE for "Theme My Login 2FA" ≠ theme-my-login; "ReviewX" ≠ "Review Schema".
Pitfalls
- Versions often DON'T leak in HTML (title-only) — don't burn requests forcing it.
- Bursting version probes (parallel curls) trips openresty rate limits fast — always sequential + sleep.
- Don't confuse "gau/crt.sh empty" with "target blocked" — those are external services.
1---2name: stealth-web-recon3description: WAF-aware, rate-limited external recon and web asset discovery for bug bounty / authorized pentest targets. Avoids triggering Cloudflare / openresty / generic WAF blocks while still mapping subdomains, panels, and tech stack.4---56# Stealth Web Recon — WAF-Aware Asset Discovery78## Purpose9Map a target's external attack surface (subdomains, live web services, exposed panels, tech stack) WITHOUT getting blocked by Cloudflare, openresty rate limits, or other WAFs. The user explicitly requires blocks to be avoided — treat "don't trigger the WAF" as a hard constraint, not a nice-to-have.1011This is a companion to the `recon` skill: use `recon` for the generic flow, this skill for the STEALTH discipline and command set that keeps you under WAF radar.1213## Core principle: passive first, active last, never burst141. **Passive enumeration** (crt.sh, subfinder, gau/Wayback) gathers hosts with zero requests to the target.152. **DNS resolution** (dnsx) is borderline-passive — uses public resolvers, but space the queries.163. **Active HTTP probing** (httpx) is the only step that "hits" the target — keep it slow and rate-limited.174. **Content scanning** (katana/ffuf/arjun) is skipped entirely on WAF/Bot-Management hosts.1819## Workflow (worked, zero-block sequence)20See `references/stealth-recon.md` for the exact command set and `references/katana_pitfalls.md` for katana crawl gotchas. Summary:21221. **Subdomain discovery (passive):**23 `subfinder -d <target> -silent -recursive` → then crt.sh JSON (external, may rate-limit — retry once or discard).242. **DNS resolve:** `dnsx -l subfinder.txt -silent -r 8.8.8.8,1.1.1.1`.253. **httpx probe (THE active step, rate-limited):**26 `httpx -l resolved.txt -silent -t 30 -rate-limit 15 -timeout 8 -ports 80,443 -title -status-code -tech-detect -server -follow-redirects`27 - `-rate-limit 15` and `-t 30` are mandatory. Never run httpx at default concurrency against a WAF host.28 - `-tech-detect` is what reveals Cloudflare Bot Management so you can exclude those hosts.294. **Passive URL mining:** `gau --subs <target>` (Wayback, external — empty output is NORMAL, not a failure).305. **Panel version confirmation:** ONE slow `curl --max-time 15 -A "Mozilla/5.0"` per host, sequential, `sleep 1` between — never a burst.3132## WAF-exclusion rule (critical)33If httpx tech-detect shows `Cloudflare` + `Cloudflare Bot Management`, do **header-only / passive checks** on that host. Do NOT run katana/ffuf/arjun against it. Bot Management is the fastest path to a block. (Sensitive / Bot-Managed subdomains like admissions, enterprise assets, or core LMS should be excluded from aggressive content scanning.)3435## Abort / back-off signals36- HTTP `429` or a JS challenge page → STOP active scanning on that host, fall back to passive-only.37- External tools (crt.sh, gau) returning empty → non-critical, discard. Never compensate by hammering the target.3839## Crawl pitfalls (cost real wasted cycles — verified)40- **katana `-silent` discards ALL results.** In v1.7.0, `-silent` routes crawl output to **stderr**; `katana ... -silent > file 2>/dev/null` yields a 0-byte file (exit 0, looks fine). Fix: use katana's own `-o file` (no `-silent`), OR drop `-silent` and capture stdout (`> out.txt 2>err.txt`). Full repro in `references/katana_pitfalls.md`.41- **Headless katana empty.** `-jc -kf all` needs Chromium; absent → exit 0, 0 endpoints. Use standard HTTP crawl (`-d 2 -rate-limit 8 -delay 1000`) unless Chromium is confirmed present.42- **Background katana SIGINT truncation.** A background `katana ... > file` whose parent shell gets SIGINT truncates output ("Ctrl+C pressed" in stderr). Wrap as `setsid bash -c 'katana ... > file 2>err.txt; echo DONE >> file'` so the child survives the parent signal group.43- Recover subdomains missed by subfinder from crawl links (Moodle/OJS/SaaS hosts referenced inside page HTML, e.g. `lms`, `research`, `journal`).4445## Read-only default-credential validation (extends F-01 panel exposure)46When a panel is publicly reachable (HTTP 200 login), validate auth strength WITHOUT brute force:47- Max **3 common pairs** per panel via its REAL login endpoint (form POST for Adminer/Proxmox/NPM; JSON `{"email"/"username","password"}` for n8n/Kuma/DocuSeal).48- Inspect **body**, not just HTTP code (login failures often still return 200 with a re-rendered form). Markers: 401/400/"invalid"/"Invalid"/re-rendered login form = fail; a session/auth cookie or dashboard HTML = success.49- A `404` on the login path means the route is wrong (CSRF/session needed), NOT a success — report as inconclusive, do not keep guessing paths (that drifts into brute force).50- If default creds succeed → report Critical and STOP (no post-auth exploitation). If all fail → F-01 stays High (public exposure), not Critical.51- Never use enumerated usernames (e.g. from WP `/wp-json/wp/v2/users`) to attack other login surfaces — that is spraying, out of scope.52- Exposed admin/automation panels without visible auth: Adminer, n8n, Uptime Kuma, Nginx Proxy Manager, DocuSeal, Proxmox, Portainer, Nessus.53- `nginx/1.14.0 (Ubuntu)` default pages = server version leak + likely EOL.54- 502/500 on `*.dev-app.*` and staging hosts = dead dev infrastructure exposed publicly.55- Missing security headers (X-Frame-Options, X-Content-Type-Options, CSP, Referrer-Policy) even when HSTS is present.56- WordPress version / readme.html leaks.5758## CVE version-matching (closes the audit loop)59After mapping versions, check them against known CVEs WITHOUT false positives — NVD 2.0 parsing + CPE affected-range check + sibling-plugin filtering. This is what turns "we saw a plugin version" into a credible (or cleared) finding. Full technique: `references/cve-version-matching.md`. Key traps: WPScan now needs a token; a CVE for "Theme My Login **2FA**" ≠ `theme-my-login`; "Review**X**" ≠ "Review Schema".6061## Pitfalls62- Versions often DON'T leak in HTML (title-only) — don't burn requests forcing it.63- Bursting version probes (parallel curls) trips openresty rate limits fast — always sequential + sleep.64- Don't confuse "gau/crt.sh empty" with "target blocked" — those are external services.