One-command, passive-first bug bounty recon
The first day of any bug bounty engagement is always the same six moves: pull subdomains, check whether the domain fakes DNS answers, resolve what came back, see what actually serves HTTP, look at what those endpoints run, and write somewhere before the terminal scrollback buries it. I ran that loop by hand, per target, for long enough to get tired of it — so I packaged it.
bb_workflow
is that loop as a single command. Free, open source (MIT), Python, and
deliberately boring in the places where a recon tool should be boring.
Passive by default, active only on request
This is the design decision the whole tool hangs on. Bug bounty rules are uneven about scanning, but they are never casual about it — an active scan against something the program did not list is how an account gets triage suspended. So the default run is passive:
No port scan, no directory busting, no vulnerability scanning unless you ask for each one explicitly:
And if you passed those flags on autopilot and then remembered the program only allows passive testing, there is a hard override:
--passive-only
strips any active phase from the run, even ones you passed on the
command line. It exists because the safe version of a command should be
the easiest one to reach for, not the one you have to reconstruct from
memory.
The phases
| Phase | What it does | Default? |
|---|---|---|
subdomains |
passive discovery from crt.sh, amass and (if present) theHarvester — merged, deduplicated | yes |
wildcard |
probes a random label to detect wildcard DNS, so you know when your subdomain list is padded with noise | yes |
resolve |
DNS resolution of every candidate host | yes |
webprobe |
parallel HTTP(S) probing — status, final URL, title, headers, built-in tech signatures | yes |
techdetect |
whatweb fingerprinting of live endpoints | yes |
portscan |
nmap top-ports against resolved hosts | opt-in |
dirbust |
gobuster directory busting | opt-in |
vulnscan |
nuclei against live endpoints | opt-in |
Every phase can also be addressed directly — --steps subdomains webprobe runs only what you name, --skip portscan
drops what you do not want. The external tools (amass, nmap, whatweb,
gobuster, nuclei) are auto-detected and gracefully skipped when missing;
the pipeline itself needs Python 3.9+ and requests, nothing else.
The scope file is the safety rail
Recon tools fail in a specific way: they happily enumerate and touch whatever you point them at, and scope discipline lives entirely in your head. This one takes the program's in-scope names as an argument:
scope.txt
is one domain per line, straight from the program's scope page.
Anything found outside those names is dropped before probing — and if
the target you passed is itself not covered by the file, the run refuses
to start and exits with an error instead of quietly proceeding. The
wildcard check serves the same discipline from the other direction:
knowing that *.target.com answers for every fake label saves you from celebrating a subdomain list that is mostly DNS fiction.
The output is the point
Everything lands in reports/<target>/:
summary.md is the triage view — surface counts, live endpoints with titles and status codes, findings if any. report.json holds the same data machine-readable, so a later script can consume it without scraping terminals. run.log is the audit trail of what ran, when, and what it returned. live_urls.txt feeds straight into nuclei -l when you decide the program allows a scan.
The rule I built it around: no result should exist only in your scrollback. Half of wasted bounty time is not the recon itself — it is not being able to reconstruct what you already found three hours later.
What a run actually looks like
A real default run, executed today against example.com:
Ten seconds end to end — because example.com
is a tiny surface. On a real program the same command takes longer in
proportion to what the sources return; the phases and the artifacts are
identical. The point of showing this run is that every number above came
out of the tool, not out of a draft I wrote around a wish.
What it does not do
It does not exploit anything, does not decide what is in scope for you (the program's rules do that, and the file you feed it only reflects them), and it does not replace the part of the job that actually finds bugs — reading, guessing, and chaining what the recon surfaced. It handles the repetitive half so the interesting half gets the hours.
Get it
MIT licensed. The README carries the full phase reference and the ethical-use reminders — including the only line that matters: the tool grants no permission by itself; the program's terms do.
If you run it against an in-scope target and hit a rough edge, the issue tracker on the repo is the right place — or use the contact form on the main site, that one works too, I tested it.