SeguridadReconRustBug bountyCadena de suministro

Sovereign recon: what a 4000-star tool gives you (and what is debt)

Published on 2026-09-23 · Xiliux

Someone hands you a security tool with 4000 GitHub stars and the question seems obvious: do we use it? The better question is another one: what here fits, and what is debt we would inherit without noticing?

We actually did it. We cloned a popular offensive recon tool, read its 1561 lines of source —not the README, the code— and classified every capability against a single criterion: that it be keyless and sovereign. Meaning it works without depending on a keyed API, a paid service, or a third-party scrape someone can take down tomorrow.

Almost all of it was debt

We also looked at the rest of the author's profile —twelve more repos. The only other adjacent one was a bypass of CloudFlare's anti-bot challenges... from 2019, already dead (CloudFlare moved to opaque challenges years ago). And a scraper that solves CAPTCHAs by OCR, which also crosses our charter: we do not evade bot-detection, period. Of thirteen repositories, exactly one had anything alive for us.

We kept one vein

The genuinely payable thing was one: cloud storage data exposure. An S3 or Google Cloud Storage bucket set to public-readable or listable leaks its contents —database dumps, backups, internal files— to anyone with a browser, no credentials. It is a classic data leak (CWE-200), and it keeps showing up.

The original's discovery was weak (it only looked at the page's <img src> tags). We reimplemented it in Rust, keyless, no SDK: candidates are generated by permuting the target's name (acme, acme-backup, assets-acme…), each is probed with an anonymous GET against S3 and GCS, and the response is classified by its unmistakable signal: a ListBucketResult in the body is listable; an AccessDenied is "exists but closed"; a NoSuchBucket does not exist. With its anti-false-positive control: a 200 that is NOT a ListBucketResult —a website served from the bucket— does not count as listable.

Two things that are non-negotiable, because we are a responsible-disclosure tool, not a looter:

  1. Read-only. Anonymous GET only, with a read ceiling so we never download gigabytes. It never writes, mass-lists, or exfiltrates.
  2. Ownership unconfirmed. The S3 namespace is global: acme-backups may not be your target's. Every finding is flagged so a human confirms ownership and scope before reporting. Nothing is auto-submitted.

The lesson

A star is not a fit. What you adopt from someone else's tool is a technique, not a file; and the technique gets reimplemented under your own rules —in our case, in Rust, keyless, with tests that discriminate. The rest is discarded with its reason written down, so you don't re-evaluate it in six months. Evaluating a dependency well means, most of the time, saying no.

FAQ

Why not reuse the code directly?

Because it was Python leaning on external binaries (nmap, openssl, whois) and third-party endpoints that get scraped and break. Our engine is a single Rust binary that runs on its own. You adopt the TECHNIQUE —how to detect a public bucket, say— and reimplement it; the code is not portable.

What is a 'keyless and sovereign' criterion?

That a capability does not depend on a keyed API, a paid service, or a fragile scrape someone can take down. Recon that dies when Google shows you a CAPTCHA is not recon: it is a dependency in disguise.

What did you discard, and why?

Google dorks (CAPTCHA), third-party scrapes, nmap wrappers, obsolete TLS checks (Heartbleed died in 2014), and —from the same author's other repos— a 2019 CloudFlare bypass that is already dead and a scraper that SOLVES CAPTCHAs, which also crosses our charter: we do not evade bot-detection.

What is the payable vein you did build?

Cloud storage data exposure: an S3 or GCS bucket set to public-readable/listable leaks its contents to anyone without credentials (CWE-200). We probe it with an anonymous, read-only GET and flag every finding as 'ownership unconfirmed' until a human verifies the bucket belongs to the target and is in scope.

← More articlesRequest a quote