El mapa y la ventana: cómo un escaneo agéntico encadenó una fuga de documentación hasta obtener credenciales robadas
Descubra cómo el Agentic Deep Scan de Ostorlab encadenó una divulgación de OpenAPI de severidad media con una vulnerabilidad SSRF para eludir una restricción de loopback y extraer credenciales de base de datos.
Ostorlab Agentic Deep Scan encontró una especificación OpenAPI legible públicamente en /static/openapi.json y la calificó como de severidad media: documentación, sin credenciales. Después leyó ese documento como un inventario y lo recorrió hasta llegar a dos hallazgos críticos, un conjunto de credenciales de base de datos activas y una sesión de administrador falsificada.
¿Qué es una ruta de ataque entre capas? Una ruta de ataque entre capas es aquella en la que un artefacto expuesto en una capa de una aplicación (un archivo de compilación, un recurso estático, un bundle de cliente) aporta lo necesario para alcanzar una debilidad en una capa distinta, normalmente el backend. Con frecuencia, el artefacto no es una vulnerabilidad en sí mismo; su valor está en que elimina las conjeturas al probar otra cosa.
Resumen ejecutivo (TL;DR)
Dos endpoints de esa especificación fueron decisivos. /internal/secret devuelve los secretos de la aplicación y rechaza a cualquiera que no sea el propio servidor, respondiendo a las solicitudes directas con HTTP 403 Internal resource. Loopback only. /upload_profile_picture_url acepta una URL y la descarga desde el lado del servidor.
Apuntar el segundo al primero cumplió la restricción de loopback en lugar de romperla. La respuesta incluía claves de firma de la aplicación, secretos JWT y credenciales de base de datos, y la clave de firma filtrada se utilizó después para generar un token de administrador que la aplicación aceptó.
Entorno de pruebas y metodología
Este recorrido documenta un ejercicio de benchmarking simulado y autorizado. Todas las pruebas las realizó Ostorlab Agentic Deep Scan contra vulnbank.org, una aplicación bancaria deliberadamente vulnerable, publicada y mantenida para la investigación de seguridad y la evaluación de herramientas. No intervino ningún sistema de producción, dato real de clientes ni activo de terceros, y todas las credenciales que se muestran a continuación son valores de prueba sembrados que pertenecen a ese sandbox.
Por qué los escáneres se detienen en la especificación
Las herramientas basadas en firmas evalúan una solicitud a la vez frente a una biblioteca de patrones maliciosos conocidos. Esa primitiva es rápida y determinista, y tiene tres puntos ciegos estructurales que ninguna regla adicional resuelve.
| Limitación | Por qué ocurre | Qué se pierde aquí |
|---|---|---|
| Sin estado entre solicitudes | Los endpoints se evalúan de forma aislada, solicitud por solicitud | Que dos endpoints enumerados en el mismo archivo se combinan en una elusión |
| Sin concepto de propósito | Las reglas codifican sintaxis, no para qué sirve una funcionalidad frente a lo que hace | Que un cargador de fotos de perfil es, desde la posición del servidor, un generador de solicitudes |
| Un control que funciona da por terminada la prueba | Un 403 correcto se registra como resultado negativo y el endpoint se descarta |
Que el control nombra a una parte autorizada a la que quizá se pueda llegar por otros medios |
Cada uno de esos comportamientos es correcto por separado. Descargar la especificación no produce ninguna coincidencia de firma. Sondear /internal/secret devuelve un 403 genuino. Ninguna de las dos observaciones es errónea, y ninguna produce un hallazgo.
Qué hace de forma distinta el agente
Ostorlab Agentic Deep Scan ejecuta un bucle de razonamiento continuo de cinco pasos para descubrir y validar vulnerabilidades:
- Reconocimiento: mapear la superficie: endpoints, parámetros, requisitos de autenticación, artefactos expuestos.
- Hipótesis: razonar sobre qué podría fallar a la vista de lo observado.
- Prueba: enviar la solicitud que demuestra o refuta la hipótesis.
- Validación: confirmar un impacto real en lugar de una respuesta sugerente.
- Encadenamiento: preguntarse si este resultado, combinado con cualquier cosa ya encontrada, abre una ruta que aún no se ha probado.
Los pasos 2 y 5 marcan la diferencia. Un escáner basado en firmas tiene el paso 3 y, de forma débil, el paso 4. No formula hipótesis sobre para qué sirve una funcionalidad y no conserva ningún modelo de hallazgos anteriores que combinar con el actual. Un agente mantiene un inventario continuo de la aplicación (lo que ha visto, lo que ha descartado, lo que sigue sin explicación) y lo consulta cada vez que llega un nuevo resultado. La cadena que sigue existe por completo en el paso 5.
Así ocurre: una cadena encontrada por Agentic Deep Scan
Hallazgo n.º 1 (media): especificación OpenAPI accesible públicamente
La aplicación servía un documento OpenAPI 3.0 completo desde su directorio público de recursos estáticos:
$ curl -s https://vulnbank.org/static/openapi.json | wc -l
1471
$ curl -I https://vulnbank.org/static/openapi.json
HTTP/2 200
content-type: application/json
Sin autenticación, con una ruta predecible, 1,471 líneas que describen 32 endpoints con sus esquemas y los requisitos de autenticación de cada ruta. /static/ es donde un frontend guarda hojas de estilo e imágenes, no un contrato del backend.

Figura 1: Calificado como de severidad media, y con razón en sus propios términos. La propia descripción del hallazgo ya señala la consecuencia: la especificación «revela la existencia de endpoints sensibles como /sup3r_s3cr3t_admin que, de otro modo, exigirían un rastreo exhaustivo para descubrirlos».
Al enumerarla, las 1,471 líneas se convirtieron en una superficie estructurada, que incluye rutas a las que la aplicación nunca enlaza:
| Categoría | Endpoints |
|---|---|
| Autenticación | /login, /register, /api/v1/forgot-password |
| Transacciones | /transfer, /transactions/{account_number}, /check_balance |
| Administración | /sup3r_s3cr3t_admin, /admin/create_admin, /admin/delete_account/{user_id} |
| Internos | /internal/config.json, /internal/secret, /latest/meta-data/* |
| Carga de archivos | /upload_profile_picture, /upload_profile_picture_url |

Figura 2: La especificación analizada y convertida en un inventario. El valor no está en una ruta concreta, sino en tener toda la superficie a la vista a la vez.
La hipótesis evidente, refutada correctamente. /internal/secret se anuncia a sí mismo, así que el agente lo solicitó:
$ curl -s -i https://vulnbank.org/internal/secret
HTTP/2 403
{"error": "Internal resource. Loopback only."}
Rechazado, y rechazado correctamente. El endpoint comprueba desde dónde llega la solicitud y atiende únicamente a la propia máquina. Para una herramienta que prueba los endpoints de forma independiente, este es el final del camino: hipótesis refutada, control sólido, se pasa a otra cosa.
Hallazgo n.º 2 (crítica): SSRF mediante la carga de la foto de perfil
La pregunta productiva no era cómo derrotar la comprobación de loopback, sino quién la cumple y si se puede lograr que esa parte actúe.
Loopback significa el servidor. Una fila del inventario, clasificada en la carga de archivos, describe una funcionalidad que hace que el servidor emita solicitudes salientes a demanda:
$ curl -X POST https://vulnbank.org/upload_profile_picture_url \
-H "Authorization: Bearer <JWT>" \
-H "Content-Type: application/json" \
-d '{"image_url": "http://127.0.0.1:5000/internal/secret"}'
Ahora la solicitud se origina en 127.0.0.1. La comprobación de loopback se supera: se cumple de verdad, no se esquiva.

Figura 3: El hallazgo registra su propia procedencia en la primera línea de su bloque de código: # From OpenAPI spec analysis.
{{
"secrets": {{
"app_secret_key": "secret123",
"jwt_secret": "secret123",
"env_preview": {{
"DB_HOST": "db",
"DB_NAME": "vulnerable_bank",
"DB_USER": "postgres",
"DB_PASSWORD": "postgres"
}},
"DEEPSEEK_API_KEY": "sk-e2719..."
}}
}}

Figura 4: La secuencia completa tal como se registró: registrarse, pivotar, exfiltrar. La carga se captura literalmente, no resumida.
Hallazgo n.º 3 (crítica): exposición de datos sensibles mediante /internal/secret sin protección
Una sola respuesta sugerente no es un hallazgo, así que se volvió a ejecutar la ruta para confirmar que el comportamiento era repetible y que los valores eran reales:
Intento de validación 1: la solicitud SSRF mediante
/upload_profile_picture_urlahttp://127.0.0.1:5000/internal/secretdevolvió la carga completa de secretos Intento de validación 2: credenciales confirmadas: App Secret=secret123, JWT Secret=secret123, credenciales de base de datos=postgres/postgres
Esa segunda línea es lo que convierte una respuesta en una declaración de impacto, y la ejecución no se detuvo en la lectura de la carga. El jwt_secret filtrado se utilizó para firmar un token que afirmaba {"user_id": 1, "username": "admin", "is_admin": true}, que luego se presentó a la ruta de administración que la especificación había revelado en el primer paso:
$ curl -X GET https://vulnbank.org/sup3r_s3cr3t_admin \
-H "Authorization: Bearer <token forged with secret123>"
HTTP/2 200 # full admin panel
El secreto no solo se observó en tránsito. Se utilizó para generar una credencial que la aplicación aceptó, y esa es la diferencia entre una cadena filtrada y una omisión de autenticación.

Figura 5: La descripción expone la dependencia con claridad: «Combined with the SSRF vulnerability».

Figura 6: El control y su elusión en una sola vista. El 403 es genuino. Lo que lo derrotó no fue un ataque a esa comprobación, sino un segundo endpoint de la misma especificación.
Lectura de la cadena
| Paso | Hallazgo | Severidad | Aportado por |
|---|---|---|---|
| 1 | Especificación OpenAPI servida públicamente | Media | Error de despliegue |
| 2 | SSRF mediante la descarga de la URL de la foto de perfil | Crítica | Endpoint citado en la especificación |
| 3 | Exfiltración del contenido de /internal/secret |
Crítica | El SSRF, dirigido a un objetivo citado en la misma especificación |
| 4 | Sesión de administrador falsificada con el secreto JWT filtrado | Impacto del n.º 3 | La clave de firma del paso 3, contra una ruta de administración del paso 1 |
Casi todo eso es mecánico. Descargar un archivo estático, analizar JSON, enumerar rutas, enviar una solicitud a cada una y registrar el estado: todo ello automatizable sin ningún tipo de razonamiento.
El paso que no es mecánico se sitúa entre las filas 1 y 2. La fila 1 es un inventario. La fila 3 es el objetivo. La fila 2 no es ninguna de las dos: es el reconocimiento de que una funcionalidad clasificada como «carga de archivos» es, desde la posición del servidor, un generador de solicitudes, y de que un generador de solicitudes es exactamente lo que necesita una restricción de loopback.
Nada en la especificación dice eso. El documento describe /upload_profile_picture_url como una carga de imágenes, porque para eso sirve. Leerlo como un pivote implica tener en mente dos filas sin relación a la vez y preguntarse qué puede hacer una con la otra.
Dónde reside realmente la severidad
Resulta tentador subir la calificación de la divulgación de la especificación ahora que dos hallazgos críticos se remontan a ella. Sería un error, y el agente no lo cometió.
La mayoría de los endpoints de esa especificación estaban bien. Enumerarlos no arrojó nada, porque sus controles resistieron. El propio /internal/secret devolvió un 403 correcto ante el acceso directo. La especificación no creó el SSRF ni debilitó la comprobación de loopback.
Lo que cambió fue el coste. Convirtió una búsqueda a ciegas por una superficie de API desconocida en un ejercicio dirigido contra un mapa completo, y hizo visible de un vistazo la relación entre dos endpoints.
Esa distinción orienta la corrección. Si se elimina la especificación, el SSRF sigue funcionando, la elusión del loopback sigue funcionando y /internal/secret sigue entregando credenciales de base de datos a cualquiera que pueda alcanzarlo desde dentro. El documento no debería ser público, pero corregirlo soluciona el descubrimiento, no la exposición.
Preguntas frecuentes
¿Por qué una especificación OpenAPI expuesta es un riesgo de seguridad? Rara vez contiene credenciales, razón por la cual casi nunca se califica por encima de media por sí sola. Su riesgo es que publica el inventario completo de endpoints, incluidas rutas a las que la interfaz nunca enlaza, con los esquemas de parámetros y los requisitos de autenticación de cada endpoint. Eso convierte una búsqueda a ciegas por una superficie de API desconocida en un ejercicio dirigido contra un mapa conocido, y hace visibles relaciones entre endpoints que, de otro modo, exigirían un rastreo exhaustivo para encontrarse.
¿Cómo elude un SSRF una restricción de solo loopback? No la elude; la cumple. Un endpoint restringido a loopback comprueba desde dónde se origina la solicitud y atiende únicamente al propio servidor. Una vulnerabilidad de falsificación de solicitudes del lado del servidor (SSRF) permite a un atacante aportar una URL que el servidor descarga después, de modo que la solicitud resultante se origina realmente en 127.0.0.1. El control se evalúa correctamente y la permite, y por eso las restricciones de loopback no bastan por sí solas cuando cualquier endpoint de la misma aplicación descarga URL proporcionadas por el usuario.
¿Debe escalarse un hallazgo de baja severidad cuando conduce a uno crítico? Normalmente no. Aquí, la divulgación de la especificación no creó el SSRF ni debilitó la comprobación de loopback; ambas vulnerabilidades existían de forma independiente. Lo que cambió fue el coste del descubrimiento. Eliminarla no cerraría ninguno de los dos hallazgos críticos, y escalarla tiende a desviar la corrección hacia la divulgación en lugar de hacia el defecto explotable.
¿Qué distingue el escaneo agéntico del escaneo basado en firmas? Un escáner basado en firmas evalúa los endpoints de forma aislada y no conserva ninguna memoria que conecte un resultado con el siguiente. Descargar la especificación no produce ninguna coincidencia; sondear /internal/secret devuelve un 403 correcto, registrado con exactitud como resultado negativo. Ninguna de las dos observaciones es errónea. El hallazgo existe solo en la relación entre dos endpoints enumerados en el mismo archivo, lo que exige mantener un modelo de la superficie a lo largo de las solicitudes.
¿Se realizaron estas pruebas contra un sistema de producción? No. Todas las pruebas se realizaron contra vulnbank.org, una aplicación deliberadamente vulnerable publicada para la investigación de seguridad y la evaluación comparativa de herramientas. Las credenciales que se muestran son valores de prueba sembrados dentro de ese sandbox.
Conclusión
Los escáneres seguirán encontrando lo que parece incorrecto en la red, y deben hacerlo: una especificación de API servida públicamente es exactamente el tipo de configuración incorrecta que una regla detecta a bajo coste. Lo que una regla no puede hacer es leer esa especificación como un plano y advertir que dos endpoints descritos en secciones distintas, con fines distintos, se combinan en una ruta para rodear un control que funciona perfectamente por sí solo.
Ese es el cambio que representan en la práctica las pruebas agénticas: no encontrar más patrones, sino mantener en memoria suficiente parte de la aplicación para ver lo que sus piezas se hacen entre sí.
Ejecute el mismo bucle de razonamiento contra su propia superficie con Ostorlab Agentic Deep Scan.