SAINT GRAIL: De Python a Go, Resultados Concretos y el Futuro del Bug Hunting Autónomo

El prototipo en Python ya funcionaba. Pero necesitaba rendimiento extremo, concurrencia real y un uso eficiente de la memoria para que el agente híbrido pudiera escalar. La migración a Go no solo cumplió eso, sino que también generó dos PoCs funcionales que demuestran el valor del sistema.

/índice +

00. Retomando el Camino

Hace unas semanas publiqué el artículo sobre SAINT GRAIL, la plataforma modular para investigación de seguridad ofensiva que construí en Python con Flask, SurrealDB y un agente híbrido que integraba DeepSeek y DeepWiki. La respuesta fue buena y el sistema demostró ser funcional, pero rápidamente me topé con los límites del enfoque.

El prototipo en Python era ágil para desarrollar, pero al escalar el número de vectores, nodos y la cantidad de datos JSON que se movían entre los módulos, el cuello de botella se hizo evidente. La latencia en la serialización/deserialización, el consumo de memoria y la imposibilidad de aprovechar el paralelismo real (GIL) empezaron a frenar el ciclo de investigación. Necesitaba un motor que pudiera procesar cientos de vectores en paralelo, parsear estructuras pesadas en milisegundos y liberar recursos para el LLM local.

La respuesta fue Go. No por moda, sino por razones técnicas muy concretas. Y los resultados no tardaron en llegar.

Contexto

Este artículo es la continuación directa de SAINT GRAIL: Una Plataforma Modular para Investigación de Seguridad Ofensiva con IA. Si no has leído el anterior, te recomiendo hacerlo para entender el origen del proyecto.

01. ¿Por qué Migrar a Go?

La decisión de migrar el orquestador central de Python a Go no fue trivial. Implicó reescribir cientos de líneas de código, adaptar la lógica de concurrencia y rediseñar la interacción con la base de datos. Pero los beneficios superaron con creces el esfuerzo.

Estas son las razones clave que me llevaron a dar el salto, basadas en las conclusiones de la investigación y la experiencia con el prototipo:

En resumen: Go convierte el orquestador en un motor de alto rendimiento que libera a la CPU y la RAM para lo que realmente importa: la inteligencia del agente y la ejecución de exploits.

Consola CLI mostrando estado de API y flujo de trabajo
saint_grail_cli_status.jpg — El nuevo CLI en Go muestra en tiempo real el estado del agente, el progreso global y el presupuesto de tokens.

02. Arquitectura en Go

La versión en Go mantiene la misma filosofía modular, pero con una estructura mucho más desacoplada y eficiente. El orquestador central (orchestrator.go) coordina todos los subsistemas mediante canales y goroutines, sin bloqueos ni esperas innecesarias.

2.1 Componentes Clave

// Estructura simplificada del orquestador en Go type Orchestrator struct { store storage.GraphStore llm llm.Router // enruta entre DeepSeek, Ollama, etc. deepwiki *deepwiki.Client fuzzing *fuzzing.Fuzzer executor *executor.BinaryExecutor tracker *utils.ExploitTracker vectorSem chan struct{} // semáforo para concurrencia wg sync.WaitGroup }

2.2 Flujo de Trabajo Mejorado

El bucle principal del agente ahora opera en ciclos de 5 segundos, ejecutando en paralelo:

Gracias a las goroutines, cada target se procesa de forma independiente, y el semáforo vectorSem limita el número de vectores concurrentes para no saturar el sistema.

Dashboard principal mostrando fases y estado global
saint_grail_horizontal_workflow.jpg — El dashboard mantiene la misma estética, pero ahora los datos se actualizan en tiempo real con la nueva API en Go.

03. Resultados Concretos: Dos PoCs Funcionales

La migración a Go no solo mejoró el rendimiento; también permitió que el sistema generara dos pruebas de concepto (PoCs) funcionales que demuestran el valor real del enfoque. Estos PoCs fueron descubiertos automáticamente por el agente durante sus ciclos de investigación y fuzzing, y posteriormente validados manualmente.

3.1 PoC #1: TOCTOU en DownloadManager de Chromium

Vulnerabilidad: Condición de carrera TOCTOU (Time-Of-Check-Time-Of-Use) en el gestor de descargas de Chromium. El sistema detectó que dos descargas con el mismo nombre, iniciadas casi simultáneamente, pueden colisionar y hacer que una trunque a la otra. Chromium reporta ambas como completadas, pero en disco solo queda un archivo.

Impacto: Suplantación de archivos descargados. Un atacante puede reemplazar un binario legítimo por uno malicioso durante la descarga, y la víctima ejecutará el archivo confiando en el gestor de descargas.

Cómo lo encontró SAINT GRAIL: El agente, al analizar el flujo de descargas en Chromium (a través de su parser AST y el análisis de código fuente), identificó la ventana de sincronización entre el hilo UI y el hilo de descarga. Generó un vector de ataque y, tras validar con DeepWiki, creó un PoC en Python que demostraba la colisión.

Nota sobre la inestabilidad

La condición de carrera depende del timing y la carga del sistema. En entornos con SSD rápido, la colisión es menos probable. Sin embargo, en discos HDD o con payloads grandes (50 MB+), la probabilidad aumenta significativamente.

3.2 PoC #2: CRC Bypass + SFX en 7‑Zip

Vulnerabilidad: Bypass de integridad (CRC) en el formato 7z. El sistema descubrió que eliminando el bloque kCRC del header del archivo, la bandera CrcDefined se vuelve false y la extracción se realiza sin verificar el CRC. Combinado con el mecanismo SFX (autoextraíble), se puede ejecutar código arbitrario al ejecutar el SFX.

Impacto: Ejecución de código asistida por el usuario (User-Assisted RCE) y persistencia a nivel de usuario (Startup folder). Afecta a 7-Zip 9.20 – 25.01 en Windows.

Cómo lo encontró SAINT GRAIL: El agente, durante un ciclo de fuzzing sobre el formato 7z, generó un archivo malformado que pasaba todas las pruebas de integridad (7z t). Al analizar el código fuente de 7-Zip (vía DeepWiki + AST), identificó que el CRC del folder se omitía si el bloque kCRC no estaba presente. El sistema construyó automáticamente un SFX que embebería el payload y lo ejecutaría.

Este hallazgo es especialmente relevante porque el bypass de integridad permite que un atacante distribuya un archivo malicioso que parece legítimo, y el mecanismo SFX asegura que el payload se ejecute sin interacción adicional. La persistencia en el Startup folder convierte el ataque en una amenaza duradera, ideal para campañas de spear‑phishing o distribución de malware.

Validación en vivo

Ejecuté poc_7zip.py en mi máquina Debian y generó un KB5012345_Installer.exe que, al ejecutarse en Windows, lanza calc.exe. El archivo .7z embebido pasa 7z t sin errores.

3.3 El Sistema Detrás de los PoCs

Estos dos PoCs no son scripts aislados; son el resultado directo del flujo de trabajo de SAINT GRAIL. El orquestador en Go orquesta el descubrimiento, la validación y la generación de exploits de forma autónoma. Cada PoC viene acompañado de un README.md que documenta el hallazgo, el mecanismo y las instrucciones de ejecución.

Todo el código generado, incluyendo los PoCs y el orquestador, está disponible en el repositorio público:

github.com/tty503/saint-grail-pocs-output

Ahí encontrarás los scripts, la documentación y las instrucciones para replicar los hallazgos.

Ejecución de PoC y verificación en /tmp
fuzztoolkit_poc_test_tmp.jpg — Ejecución del PoC en terminal y verificación del archivo generado en /tmp.
Motor de PoC en el navegador
saint_grail_browser_poc_runner.jpg — El motor de ejecución de PoCs en el navegador, mostrando el progreso en tiempo real.

04. Galería de Capturas

A continuación, una selección de capturas de pantalla que muestran diferentes aspectos del sistema en funcionamiento, desde la interfaz web hasta la línea de comandos y la validación de exploits.

Vista jerárquica del grafo y ficha técnica de fase
saint_grail_jerarquico_fase3.jpg — Disposición jerárquica del grafo con fases, vectores y brechas. Detalle de la FASE_3 (UAF Entry).
Grafo de red interactivo y mapeo MITRE
saint_grail_force_graph_v05.jpg — Layout de fuerza mostrando nodos interconectados. Inspección del nodo V05 con técnica MITRE ATT&CK asociada.

05. Estado Actual y Próximos Pasos

La versión en Go de SAINT GRAIL es completamente funcional y ha demostrado su capacidad generando dos PoCs reales. Sin embargo, el sistema sigue siendo un work in progress. Estas son las áreas en las que estoy trabajando actualmente:

Pero más allá de estas mejoras, el camino por recorrer es largo y apasionante. A continuación, detallo los principales desafíos y funcionalidades que aún están pendientes:

El objetivo a medio plazo es publicar el código completo como open-source, una vez que la documentación esté al día y los tests cubran las funcionalidades críticas. Mientras tanto, puedes seguir el progreso en el repositorio de PoCs y en mi blog.

¿Cuándo estará disponible?

Estoy apuntando a finales de 2026 para una versión beta pública. La prioridad es estabilizar el orquestador, completar la integración con modelos locales y documentar el flujo de trabajo para que otros investigadores puedan usarlo fácilmente.

/Resultados tangibles, tecnología de vanguardia

SAINT GRAIL demuestra que la combinación de IA, fuzzing y análisis estático puede generar hallazgos de seguridad reales. La migración a Go ha sido un acierto, y los dos PoCs son solo el comienzo.

06. Explicación Detallada de los PoCs

Para entender a fondo cómo SAINT GRAIL materializa sus hallazgos, desglosamos los mecanismos internos de cada exploit. Se incluyen fragmentos de código relevantes extraídos directamente de los scripts generados.

6.1 Chromium TOCTOU: Anatomía de la carrera

El PoC explota la separación entre el hilo de UI (que decide el nombre del archivo) y el hilo de descarga (que lo crea físicamente). En condiciones normales, si el archivo ya existe, Chromium añade un sufijo incremental ((1), (2)…). El fallo aparece cuando dos descargas con idéntico nombre pasan la verificación al mismo tiempo.

Flujo del ataque:

  1. Se inyecta un HTML con dos <a> ocultos en iframes, ambos apuntando a descargas diferentes pero con el mismo atributo download.
  2. Mediante BroadcastChannel se sincronizan ambos contextos: cada iframe confirma que está listo y, al recibir la orden de fuego, dispara el clic en el enlace.
  3. Ambas descargas se inician en el mismo instante, sorteando la verificación de existencia y forzando la colisión en DownloadFileImpl::Initialize.
  4. Se monitorizan los eventos de descarga vía Chrome DevTools Protocol (CDP) para detectar cuántas se reportan como completadas y cuántos archivos reales aparecen en disco.
// Fragmento del HTML inyectado: sincronización vía BroadcastChannel const bc = new BroadcastChannel("race"); bc.onmessage = (e) => { if (e.data === "FIRE") { document.getElementById("d").click(); } };

Métrica de éxito: CDP reporta ≥2 descargas completadas, pero en disco aparece exactamente 1 archivo con el nombre objetivo. Se comprueba el SHA256 del archivo superviviente: si corresponde al payload malicioso (payload B), la suplantación es efectiva.

6.2 7‑Zip CRC Bypass: Disección del formato y el parche

El formato 7z organiza la información en varios bloques. El NextHeader contiene la definición de los folders (unidades de compresión) y, opcionalmente, un bloque kCRC que almacena el CRC32 esperado para cada folder.

El bypass se logra en tres pasos:

  1. Localizar el bloque kCRC dentro del NextHeader. Su identificador es 0x0A, seguido de un byte que indica si todos los folders comparten CRC (0x01) o si hay un vector de bits (0x00).
  2. Eliminar el bloque completo (identificador + flags + datos CRC). Esto deja el array FolderCRCs vacío y fuerza CrcDefined = false.
  3. Recalcular los CRCs de integridad del header para que el archivo siga siendo sintácticamente válido. El NextHeaderCRC (CRC32 del nuevo NextHeader) y el StartHeaderCRC (CRC32 de los bytes 12-31 del archivo) se actualizan con los nuevos valores.
// Fragmento del parche: eliminación de kCRC y recálculo pos = header.find(b"\x0a\x01") // Caso AllAreDefined=1 if pos >= 0: del header[pos:pos+6] // NID(1) + flag(1) + CRC32(4) // Recalcular NextHeaderCRC next_crc = calc_crc32(header) struct.pack_into(", new_data, 28, next_crc)

Después del parche, 7z t devuelve OK porque la estructura sigue siendo correcta. Al extraer, _calcCrc se evalúa como false y la verificación nunca se ejecuta. El código de 7‑Zip CloseFile() devuelve S_OK aunque los datos extraídos no coincidan con el CRC original (inexistente).

6.3 Construcción del SFX malicioso

Para convertir la debilidad en ejecución de código, se aprovecha el modo SFX de 7‑Zip. Un ejecutable SFX estándar está formado por tres partes concatenadas:

  1. Stub SFX (7z.sfx): un pequeño binario PE que contiene la lógica de extracción.
  2. Archivo .7z parcheado: el payload empaquetado sin CRC.
  3. Configuración SFX: un bloque de texto que declara RunProgram y, opcionalmente, Directory para persistencia.
// Construcción del SFX en el PoC with open(output_exe, "wb") as f: f.write(stub_data) # 7z.sfx f.write(archive_patched) # .7z sin CRC f.write(sfx_config) # RunProgram="payload.bat"

La opción --persistence cambia el bloque de configuración para incluir Directory="%APPDATA%\...\Startup". De este modo, el payload se copia en la carpeta de inicio automático, garantizando ejecución en cada arranque.

Nota sobre la cadena de explotación

La combinación de bypass de integridad + SFX permite que un atacante distribuya un “instalador de seguridad” que extrae y ejecuta código arbitrario sin levantar sospechas. Al no modificar los tamaños ni la estructura de directorios, el archivo .7z contenido es indistinguible de uno legítimo a nivel de firma.

Ambos PoCs están públicamente disponibles en el repositorio saint-grail-pocs-output, donde se incluyen los scripts completos, instrucciones de uso y referencias a las secciones exactas del código fuente de Chromium y 7‑Zip que confirman las vulnerabilidades.

07. Referencias y Enlaces