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
- Enumeración de subdominios por dork de Google: se bloquea con un CAPTCHA. Por scrape de un servicio de terceros: frágil, y no es soberano.
- Wrappers de
nmap,openssl,whois: binarios externos que hay que tener instalados en cada máquina. Nuestro motor es un solo binario que corre solo; meter dependencias de sistema es ir hacia atrás. - Chequeos de TLS obsoletos: Heartbleed murió en 2014. El grado de una cifra no es un hallazgo que pague nadie.
- El módulo estrella de "OWASP": stubs vacíos desde 2018. Nunca se implementó.
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:
- Read-only. Solo
GETanónimo, con un techo de lectura para no descargar gigas. Nunca escribe, ni lista en masa, ni exfiltra. - Titularidad sin confirmar. El espacio de nombres de S3 es global:
acme-backupspuede 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.
Xiliux