← all writeups
Recon

Building a Recon Pipeline That Scales: From ASN to Live Attack Surface (With AI Where It Helps)

Oct 08, 2026 · 11 min · by Abhishek Bhaskar (Abhi HackZ)
Building a Recon Pipeline That Scales: From ASN to Live Attack Surface (With AI Where It Helps) — cover

Most of the bugs I report are not clever. They are ordinary bugs on hosts the target forgot about: a staging API behind a wildcard DNS record, an old admin panel on a non-standard port, a JavaScript bundle that still references a retired endpoint. The hard part is not exploitation. The hard part is being the first person to look at that host after it appeared.

That is a systems problem, not a skill problem. If you run recon by hand every time, you look at the same popular targets everyone else looks at, on the same day they do. If you run recon as a pipeline, you look at everything in scope continuously, and you get told when something changes.

This post describes the four-stage pipeline I use for both bug bounty and client VAPT (vulnerability assessment and penetration testing) work: seeding, enumeration, resolution and change detection. I will also be honest about where large language models (LLMs) help in that pipeline and where they actively make it worse.

Stage 1: seeding

Seeding answers one question: what does the organisation actually own? Every later stage depends on it, so this is where I spend disproportionate time. A missed seed is a whole branch of attack surface you will never enumerate.

The inputs I collect, in rough order of reliability:

The common misconception at this stage is that seeding is "just the root domain". People put example.com into a subdomain tool and call it done. In practice the interesting hosts live under example-corp.net, exampleinternal.io or a subsidiary brand, and only CT logs, ASN data or acquisition history will surface them.

Stage 2: enumeration

Enumeration expands each seed into candidate hosts, ports and endpoints. It should be wide and cheap. Accuracy comes in the next stage.

Subdomains

I combine passive sources (CT, DNS datasets, search engines, code hosting) with active brute force against a wordlist and permutation generation (api-staging, staging-api, api2). SubSpyder wraps this for me; the widely used open-source tools do the same job. The point is to merge every source and dedupe, because each source misses something the others catch.

One technique worth understanding rather than just running: DNS resolution of a wildcard. If *.example.com resolves to the same address, brute forcing will "find" thousands of fake hosts. Detect the wildcard first by resolving a random label, record its answer, and discard any brute-force hit with the same answer. DigIT does this check for me before anything else runs.

Ports and services

For each resolved IP, a fast port scan on the common web and management ports, then a fuller scan on hosts that look interesting. Non-standard web ports (8080, 8443, 8000, 9000, 3000) are where staging apps and dashboards live. A port is not a finding; a service version is not a finding either. They are leads. What matters is whether the service on that port is reachable from the internet, whether it should be, and what it exposes once you talk to it.

JavaScript endpoints

Modern front-ends ship their API map in the JavaScript bundle. For every live web host I pull the scripts, extract absolute and relative paths, and add every /api/..., /v2/... or GraphQL route to the endpoint list. APIScout is my wrapper for this; it is a regex pass over the bundle followed by a request to each path to see which ones answer. Retired endpoints that still answer are a recurring source of authorization bugs, because nobody maintains the access checks on code they think is dead.

# the idea, not the tool: collect scripts, extract paths, keep the ones that answer
cat live-hosts.txt | while read h; do
  curl -s "$h" | grep -oE 'src="[^"]+\.js[^"]*"' | sed 's/src="//;s/"$//' \
    | sed "s#^/#$h/#" >> scripts.txt
done
sort -u scripts.txt | xargs -n1 curl -s \
  | grep -oE '"/(api|v[0-9]+|graphql)[^"]*"' | tr -d '"' | sort -u > endpoints.txt

Stage 3: resolution and fingerprinting

Enumeration produces noise on purpose. Resolution turns it into a list of things that are actually alive, and fingerprinting tells you what each one is.

The misconception here is that more hosts means more findings. It does not. Resolution is where you throw away ninety per cent of what enumeration produced, and that is the point. The goal is a short list you will actually test, not a long list you will admire.

Stage 4: change detection

This stage is the whole reason the pipeline exists. Everything above runs on a schedule. Each run's output is diffed against the previous run, and only the delta reaches me.

The deltas, ranked by how often they turn into a report:

  1. New live host. The highest-signal event in bug bounty. A host that appeared last night has had zero people look at it.
  2. New open port on a known host. Usually a deploy exposed something: a debug port, a database admin UI, a metrics endpoint.
  3. Changed JavaScript bundle. New endpoints, new feature flags, sometimes new credentials someone committed by mistake.
  4. Changed response on a known endpoint. A 403 that became a 200 is an authorization regression. These are my favourite, because they are real bugs that will be fixed quickly once reported.
  5. New certificate in CT. Often the earliest signal that a host is about to exist. By the time DNS points at it, you already know the name.

Diffing is simple to implement and easy to get wrong. Normalise before you compare: strip timestamps, request IDs and CSRF tokens from responses, sort lists, and ignore ordering. Otherwise every run produces a hundred fake changes and you stop reading the alerts, which is the same as not having a pipeline.

# nightly: run the stages, then diff against yesterday and alert on the delta
#!/usr/bin/env bash
set -euo pipefail
DAY=$(date +%F); PREV=$(date -d yesterday +%F)
mkdir -p runs/$DAY
seed      targets.txt            > runs/$DAY/seeds.txt
enumerate runs/$DAY/seeds.txt    > runs/$DAY/candidates.txt
resolve   runs/$DAY/candidates.txt > runs/$DAY/live.txt
comm -13 <(sort runs/$PREV/live.txt) <(sort runs/$DAY/live.txt) > runs/$DAY/new-hosts.txt
[ -s runs/$DAY/new-hosts.txt ] && notify "new hosts for $DAY" runs/$DAY/new-hosts.txt

The commands are placeholders for whatever tools you prefer. The structure is what matters: each stage reads the previous stage's file and writes its own, so any stage can be re-run or replaced without touching the rest.

Where AI helps, and where it hurts

I use LLMs inside this pipeline in three places, all of them after the data has been collected and all of them as a reader, not a decision-maker.

Where it helps

Where it hurts

The misconception to avoid: "AI recon" as a product category mostly means an LLM wrapped around the same tools, with the scope check removed. The tools were never the bottleneck. Reading the output was. Use the model for reading.

Scope and ethics

A pipeline that runs unattended can do damage unattended. Three rules I enforce in code, not in my head:

The same pipeline, pointed at a client's assets under a signed VAPT engagement, becomes continuous attack-surface monitoring. The only thing that changes is the scope file.

Checklist

Takeaways

Learn more

Related: Bug Bounty in 2026 · Testing AI Agents and MCP Servers. Want continuous attack-surface monitoring on your own assets? Book a free 30-minute scoping call.

KEEP READING
Abhishek Bhaskar (Abhi HackZ)
Security Analyst & VAPT Engineer
Book a free 30-min security scoping call →