Bug bounty hunting has a dirty secret: 80% of the work is reconnaissance, and most of it is boring, repetitive, and perfect for automation. While other hunters manually click through subdomain lists, you can have a Python pipeline doing the grunt work 24/7 — and wake up to a fresh list of interesting targets every morning.
In this tutorial, I'll walk you through the recon pipeline I run on a Raspberry Pi, and how a few hundred lines of Python turned a weekend of manual work into a daily automated sweep.
The Problem With Manual Recon
A typical recon session looks like this:
- Run
subfinder/amassto enumerate subdomains - Probe which hosts are actually alive
- Grab HTTP titles, status codes, and TLS certs
- Fingerprint the tech stack (is it WordPress? An S3 bucket? An admin panel?)
- Stare at the results and pick something juicy
Steps 1–4 are pure mechanics. If you're doing them by hand every day, you're losing hours — and worse, you're inconsistent. Automation fixes both.
Step 1: Subdomain Enumeration on a Schedule
I keep a target list in targets.txt and let subfinder chew through it on a cron schedule:
# crontab -e
0 3 * * * subfinder -dL /home/pi/bbrecon/targets.txt -o /home/pi/bbrecon/data/subs_$(date +\%F).txt -silent
Running it at 3 AM means fresh results are ready with my morning coffee. On a Raspberry Pi 4 this is completely fine — subfinder is mostly network-bound, not CPU-bound.
Step 2: Probe for Live Hosts
Enumerated subdomains are mostly noise. The signal comes from probing. I use httpx for this, but here's the core idea in pure Python so you can extend it:
import asyncio
import aiohttp
async def probe(session, url):
try:
async with session.head(url, timeout=aiohttp.ClientTimeout(total=8),
ssl=False, allow_redirects=True) as r:
return {"url": url, "status": r.status, "final": str(r.url)}
except Exception:
return None
async def main(subdomains):
async with aiohttp.ClientSession() as session:
tasks = [probe(session, f"https://{s}") for s in subdomains]
results = await asyncio.gather(*tasks)
return [r for r in results if r]
alive = asyncio.run(main(open("subs_today.txt").read().splitlines()))
Concurrency matters here — 50 parallel workers will sweep 10,000 subdomains in a few minutes.
Step 3: Fingerprint What Matters
A live host isn't interesting by itself. You want to know what it is. For each alive URL, I fetch the page and extract:
- Page title — titles like "Dashboard", "Admin", or "Welcome to nginx" are instant triage
- Server header — outdated or unusual servers are worth a look
-
Known signatures —
wp-contentmeans WordPress,/_next/means Next.js,X-Amz-Bucketmeans S3
INTERESTING = ["admin", "dashboard", "login", "staging", "dev.", "internal", "jenkins", "grafana"]
def triage(result, html, headers):
flags = []
title = extract_title(html)
if any(k in title.lower() for k in INTERESTING):
flags.append("interesting-title")
if headers.get("Server", "").startswith(("nginx/1.1", "Apache/2.2")):
flags.append("old-server")
if "wp-content" in html:
flags.append("wordpress")
return flags
Everything gets written to a JSON-lines file, one entry per host, so I can query it later with jq:
jq -c 'select(.flags | contains(["interesting-title"]))' results.jsonl
Step 4: Alerts, Not Dashboards
Don't build a dashboard. Build a filter that messages you. My pipeline diffs today's results against yesterday's and sends a Telegram message for anything new — a new subdomain, a newly open port, a title change. New stuff is where the bugs are, because it hasn't been tested yet.
new_hosts = today_hosts - yesterday_hosts
if new_hosts:
send_telegram(f"🆕 {len(new_hosts)} new hosts on {target}: {list(new_hosts)[:5]}")
The Full Kit
The pipeline above is the skeleton — the production version handles rate limiting, retries, screenshotting, nuclei template runs, and state tracking across dozens of targets. Wrapping all of that yourself is a solid week of work, so I packaged everything I actually use into a Bug Bounty Automation Kit: the full recon scripts, cron templates, alerting glue, and my triage rules — ready to drop onto a Pi or a VPS.
Final Tips
- Stay in scope. Automation makes it trivial to accidentally hammer something you shouldn't. Whitelist domains, respect robots and rate limits, and read the program policy twice.
- Small VPS > your laptop for 24/7 runs. A Raspberry Pi works great and costs nothing to run.
- Recon is a compound interest game. Day one you find nothing. Day thirty, your diff alerts start catching assets nobody else has seen yet.
Automation won't find the bugs for you — but it guarantees you're always looking at the freshest attack surface, while you spend your actual brainpower on exploitation. That's the edge.
Happy hunting! 🐛
Top comments (0)