SeguridadReconRustBug bountyCadena de suministro

Recon soberano: qué nos sirve de una herramienta de 4000 estrellas (y qué es deuda)

Publicado el 2026-09-23 · Xiliux

Alguien te pasa una herramienta de seguridad con 4000 estrellas en GitHub y la pregunta parece obvia: ¿la usamos? La pregunta buena es otra: ¿qué de esto encaja, y qué es deuda que heredaríamos sin darnos cuenta?

Lo hicimos de verdad. Clonamos un recon ofensivo popular, leímos sus 1561 líneas de fuente —no el README, el código— y clasificamos cada capacidad contra un solo criterio: que fuera keyless y soberano. Es decir, que funcione sin depender de una API con llave, un servicio de pago, o un scrape de un tercero que alguien puede tumbar mañana.

Casi todo era deuda

Miramos también el resto del perfil del autor —doce repos más—. El único otro adyacente era un bypass de los retos anti-bot de CloudFlare... de 2019, ya muerto (CloudFlare cambió a retos opacos hace años). Y un scraper que resuelve CAPTCHAs por OCR, que además cruza nuestro estatuto: no evadimos bot-detection, punto. De trece repositorios, uno solo tenía algo vivo para nosotros.

Nos quedamos con una veta

Lo pagable de verdad era una cosa: la exposición de datos en almacenamiento en la nube. Un bucket de S3 o Google Cloud Storage configurado como público-legible o listable filtra su contenido —copias de bases de datos, respaldos, archivos internos— a cualquiera con un navegador, sin credenciales. Es una fuga de datos clásica (CWE-200), y sigue apareciendo.

La técnica de descubrimiento del original era débil (solo miraba los <img src> de la página). La reimplementamos en Rust, sin llaves ni SDK: se generan candidatos permutando el nombre del objetivo (acme, acme-backup, assets-acme…), se prueba cada uno con un GET anónimo contra S3 y GCS, y se clasifica la respuesta por su señal inequívoca: un ListBucketResult en el cuerpo es listable; un AccessDenied es "existe pero cerrado"; un NoSuchBucket no existe. Con su control anti-falso-positivo: un 200 que NO es un ListBucketResult —un sitio web servido desde el bucket— no cuenta como listable.

Dos cosas que no se negocian, porque somos una herramienta de divulgación responsable, no un saqueador:

  1. Read-only. Solo GET anónimo, con un techo de lectura para no descargar gigas. Nunca escribe, ni lista en masa, ni exfiltra.
  2. Titularidad sin confirmar. El espacio de nombres de S3 es global: acme-backups puede no ser de tu objetivo. Cada hallazgo sale marcado para que un humano confirme la pertenencia y el alcance antes de reportar. No se auto-envía nada.

La lección

Una estrella no es un encaje. Lo que se adopta de una herramienta ajena es una técnica, no un archivo; y la técnica se reimplementa bajo tus propias reglas —en nuestro caso, en Rust, sin llaves, con pruebas que discriminan—. El resto se descarta con su razón escrita, para no volver a evaluarlo dentro de seis meses. Evaluar bien una dependencia es, la mayoría de las veces, decir que no.

Preguntas frecuentes

¿Por qué no reusar el código directamente?

Porque estaba escrito en Python apoyándose en binarios externos (nmap, openssl, whois) y en endpoints de terceros que se raspan y se rompen. Nuestro motor es un binario en Rust que corre solo. Se adopta la TÉCNICA —cómo se detecta un bucket público, por ejemplo— y se reimplementa; el código no es portable.

¿Qué es un criterio 'keyless y soberano'?

Que la capacidad no dependa de una API con llave, un servicio de pago o un scrape frágil que alguien puede tumbar. Un recon que se cae cuando Google te pone un CAPTCHA no es recon: es una dependencia con disfraz.

¿Qué se descartó y por qué?

Dorks de Google (CAPTCHA), scrape de servicios de terceros, wrappers de nmap, chequeos de TLS obsoletos (Heartbleed murió en 2014) y —de otros repos del mismo autor— un bypass de CloudFlare de 2019 ya muerto y un scraper que RESUELVE CAPTCHAs, que además cruza nuestro estatuto: no evadimos bot-detection.

¿Qué es la veta pagable que sí construimos?

La exposición de datos en almacenamiento en la nube: un bucket S3 o GCS configurado como público-legible/listable filtra su contenido a cualquiera sin credenciales (CWE-200). Lo probamos con un GET anónimo, read-only, y marcamos cada hallazgo como 'titularidad sin confirmar' hasta que un humano confirme que el bucket es del objetivo y está en alcance.

← Ver más artículosCotizar un proyecto