✦ H3LL0 W0RLD - HACK THE PLANET - FOLLOW THE WHITE RABBIT ✦
← back to projects
Security● online

recon-ng

node://recon-ng

recon-ng is the intel-gathering front end of my security toolchain. It is a modular, CLI-driven reconnaissance framework: you give it a domain, a company, or a person, and it works outward through public sources — certificate logs, DNS, WHOIS, breach indexes, search engines — filing everything it learns into a database you can query.

It is not an exploitation tool, and that distinction is the whole point. This half of the work is about knowing what exists before anyone touches anything. I run a fork of the upstream project locally, version 5.1.2, with the full module set installed.

Modules are the unit of work

Every capability is a module, and every module does exactly one thing. The naming convention is the interface:

text
recon/domains-hosts/certificate_transparency
     └──────┬─────┘
       takes domains, produces hosts

Which means modules compose without knowing about each other. Feed a domain to certificate_transparency and you get hosts. Feed those hosts to recon/hosts-hosts/resolve and you get addresses. Feed those to a reporting module and you get something you can hand to a person. There are 109 installed here, across recon, discovery, import, reporting, and a small exploitation corner I leave alone.

The glue is the workspace: one SQLite database per engagement, which every module reads from and writes to. That is what makes chaining work — a module that needs hosts does not care which module found them, only that they are in the table.

bash
./recon-ng                 # interactive console
./recon-ng -w acme-corp    # open a workspace
./recon-cli -h             # the scriptable entry point
./recon-web                # local Flask UI over the database

The console is a trap

The interactive console is what everyone sees first, and it is the wrong way to use this seriously. Typing into a prompt produces exactly one thing: scrollback that has to be copied somewhere before it is lost.

recon-cli is the entry point worth learning. It takes a workspace, a module, that module's options, and a run flag, and then it exits:

bash
./recon-cli -w acme-corp \
  -m recon/domains-hosts/certificate_transparency \
  -o SOURCE=example.com \
  -x

That is a shell script, a launchd job, or something an agent can drive. The same sweep, the same way, every time — which is the difference between reconnaissance and poking around.

One flag worth committing to memory: the run flag on recon-cli is -x. It is -r on the interactive binary, for resource files, and mixing them up is a rite of passage.

Certificate transparency, or: the free lunch

If I could show someone one module to explain passive recon, it would be this one. Every TLS certificate issued for a domain is published to public, append-only logs so that misissued certificates can be caught. Those logs are readable by anyone. So without sending a single packet to the target, you can ask what certificates exist and read the names on them.

Pointed at IANA's reserved example.com, one module call returns twelve hosts and two contacts:

text
  | rowid |         host         |          module          |
  +----------------------------------------------------------
  | 1     | *.example.com        | certificate_transparency |
  | 2     | example.com          | certificate_transparency |
  | 3     | www.example.com      | certificate_transparency |
  | 5     | m.example.com        | certificate_transparency |
  | 6     | dev.example.com      | certificate_transparency |
  | 7     | products.example.com | certificate_transparency |
  | 8     | support.example.com  | certificate_transparency |

dev and support are the interesting rows. Nobody publishes a list of their internal-ish hostnames on purpose. They publish certificates, and the names come along for the ride. On real engagements that column is where the surprises live — the staging box someone forgot, the admin panel that was only ever meant for the office.

The same pass populates a contacts table too, because certificates carry addresses. Hosts and contacts accumulate in the same workspace, and the next module picks up where this one stopped.

Scope is the feature

Passive means it queries third parties about a target rather than touching the target. That keeps it quiet and keeps it legal — but the tool has no opinion about who you point it at, so the constraint has to be self-imposed. It only ever gets aimed at assets I own or engagements I am authorized to work, and the moment a module starts doing something active, it moves to a different part of the toolchain with different rules.

There is a structural decision here I would repeat on any project like this: findings live outside the repository. Scan output is written to a directory the git tree does not cover, so there is no version of this where a workspace database or a client's subdomain list ends up in a commit. The code is public. The results are not, and they cannot accidentally become so.

recon-ng/          # public repo: framework, setup notes, skills
~/.recon-ng/       # workspaces, module cache, API keys — never committed
recon-ng-findings/ # exported reports, outside the tree entirely

That separation also survives a reinstall. Blowing away and re-cloning the repo never touches a workspace or a key, because they were never in it.

Driving it from Claude

Because the CLI is deterministic and scriptable, it is a good fit for an agent. I wrote a Claude Code skill that lives in the repo: it picks modules appropriate to the target type, runs the sweep, works around the ones that need API keys I have not set, and turns the resulting workspace into a readable report rather than a table dump.

The division of labour is the useful part. The model decides which reconnaissance is worth doing and how to interpret what comes back — judgement, and genuinely hard. The framework does the reconnaissance identically every time — mechanism, and something a model should never be improvising.

There are two copies of the skill in the repo, and keeping them apart was deliberate:

  • `recon-ng-osint` — what I use daily. This machine's paths are baked in, and it mirrors results into my private notes.
  • `shareable-recon-osint` — genericized, no hardcoded paths, meant for handing to someone else. It pointedly does not include the note-mirroring.

The moment a shareable tool knows about your filesystem, it stops being shareable. Splitting it costs a little duplication and removes an entire category of embarrassing pull request.

Where it stands

Working and in regular use. All 109 modules are installed and enabled, a handful of API keys are configured for the ones that need them, and a large share of the useful surface — certificate logs, WHOIS, DNS resolution, every reporting format — needs no key at all. The gaps are known: a few paid sources I have not signed up for, and one dependency pinned to an older release because the current one breaks the modules that use it.

The fork is public at github.com/anonunion47/recon-ng, with the setup notes and both skills committed alongside it. What is not there, by design, is a single byte of anything it has ever found.