Given *.example.com, the naive move is to point every tool at everything and drown in output. A scope that size needs a pipeline, not a scan. Here’s the shape I use.

Stage 1 — Passive enumeration (loud tools off)

Start with sources that never touch the target:

subfinder -d example.com -all -silent \
  | anew subs.txt

Passive-first means you’ve built a candidate list before the target has logged a single one of your packets.

Stage 2 — Resolve, don’t assume

Most subdomains are dead. Resolve before you probe:

dnsx -l subs.txt -silent -a -resp \
  | tee resolved.txt \
  | awk '{print $1}' | sort -u > live-hosts.txt

Stage 3 — Probe the web surface

Now fingerprint what’s actually serving HTTP. Capture status, title, tech, and screenshot in one pass:

httpx -l live-hosts.txt -silent \
  -status-code -title -tech-detect -web-server \
  -json -o httpx.jsonl

Stage 4 — Triage by interestingness

This is where humans beat automation. Sort the surface by signal:

  • Non-200s that leak401, 403, 500 on admin-looking paths.
  • Odd tech — an old framework version, a dev server, a debug banner.
  • Orphans — hosts that don’t look like the main app: staging., internal., legacy..
# surface the weird stuff first
jq -r 'select(.tech != null) | "\(.status_code)\t\(.url)\t\(.tech|join(","))"' httpx.jsonl \
  | sort

The whole point of the pipeline is to spend your manual time only on the ~5% of hosts that are actually interesting. Everything upstream exists to shrink the pile.

Be a good guest

  • Keep a hard allow-list of in-scope roots and diff every run against it.
  • Rate-limit. A single honest IP that respects limits outlives a fleet of proxies that don’t.
  • Tag your traffic per the program’s rules so blue teams can tell research from attack.

Passive → resolve → probe → triage. Each stage throws away more than it keeps, and what survives is worth your attention.