flawopen.com/AI Security/mlflow-webhook-ssrf-cve-2026-64849
●
CVE-2026-64849 · CVSS 8.6 · 높음
보안 연구 · FlawOpen
CVE-2026-64849: MLflow Webhook SSRF 취약점
CVE-2026-64849는 MLflow webhook 전달 기능의 인증 없는 서버 측 요청 위조(SSRF)입니다 (High, CVSS 3.1: 8.6). 최초 URL 검사는 리디렉션 대상이나 실제 연결 주소를 제한하지 않았고, 테스트 endpoint가 상위 응답 본문을 호출자에게 반환할 수 있었습니다.[1]
💡 알기 쉬운 설명 (ELI5)
접수 담당자가 소포 주소를 확인했지만 전달 메모의 새 주소는 확인하지 않고 배송원이 따르게 하는 상황을 떠올려 보세요. MLflow는 연결 전에 webhook URL을 확인했지만 리디렉션과 두 번째 DNS 조회가 요청을 다른 곳으로 보낼 수 있었습니다. 수정은 HTTP 데이터를 보내기 전에 실제 연결된 peer 주소를 확인합니다.[1], [2]
핵심 개념 및 용어
SSRF- 호출자가 선택한 네트워크 요청을 서버가 대신 실행해 서버의 네트워크 신뢰 경계를 넘는 문제.[1]
Webhook 테스트 endpoint- 설정된 webhook으로 테스트 이벤트를 보내는 MLflow API. 영향을 받는 기본 서버는 상위 응답 본문을 호출자에게 반환했습니다.[1]
프레임워크 매핑
- 이 가이드에서 설명하는 약점의 CWE 참조: CWE-918
- D3-OTF 아웃바운드 트래픽 필터링 — 이 가이드의 아웃바운드 네트워크 제한은 서비스 또는 호스트가 접근할 수 있는 대상을 줄입니다.
- CISA KEV — 추가일 2026-08-19 — CVE-2026-64849
단계별 공격 실행 흐름
소스 코드 비교: 취약한 구현 vs 보안 패치
코드 조각은 URL 검증과 요청 사이의 경계 및 연결 시 peer 확인을 보여줍니다. 공격 요청과 내부 대상은 포함하지 않습니다. 3.16.0의 IPv6 정규화는 이후 별도로 추가된 강화입니다.[2]
취약한 구현
# PR #24258 이전 mlflow/webhooks/delivery.py 발췌입니다.
_validate_webhook_url(webhook.url) # checks the initial URL only
return session.post(
webhook.url, data=payload_bytes, headers=headers, timeout=timeout
) # redirects follow by default; the validated IP is not pinned
보안 강화 패치
# 연결 시 피어를 확인하는 upstream 수정 사항의 발췌입니다.
def _assert_public_peer(sock):
if _MLFLOW_WEBHOOK_ALLOW_PRIVATE_IPS.get():
return
try:
peer_ip = sock.getpeername()[0]
except OSError as exc:
sock.close()
raise SSRFProtectionError(
f"Could not determine webhook connection peer address: {exc}"
) from exc
try:
ip = ipaddress.ip_address(peer_ip)
except ValueError as exc:
sock.close()
raise SSRFProtectionError(
f"Webhook connection resolved to an invalid IP address: {peer_ip!r}"
) from exc
if not ip.is_global:
sock.close()
raise SSRFProtectionError(
f"Webhook connection blocked: {ip} is not a public IP address. "
"This may indicate a DNS rebinding attempt."
)
class _SSRFProtectedHTTPConnection(HTTPConnection):
def _new_conn(self):
sock = super()._new_conn()
_assert_public_peer(sock)
return sock
class _SSRFProtectedHTTPSConnection(HTTPSConnection):
def _new_conn(self):
sock = super()._new_conn()
_assert_public_peer(sock)
return sock
adapter = SSRFProtectedHTTPAdapter(max_retries=retry_strategy)
session = requests.Session()
session.trust_env = False
session.mount("http://", adapter)
session.mount("https://", adapter)
엔지니어링 및 시스템 보안 강화 체크리스트
- ✓CVE-2026-64849에 대해 MLflow 3.10.0–3.14.x를 최소 3.15.0으로 업데이트하고 현재 지원 릴리스를 우선 적용하세요. IPv6 전환 기능이 쓰이는 환경에는 3.16.0 이상을 배포하세요.[1], [3], [6]
- ✓Tracking Server가 신뢰하지 않는 네트워크에서 접근 가능한지 확인하고 선택형 인증 플러그인 및 권한 검사가 활성화됐는지 확인하세요. 네트워크 제한은 업데이트를 대체하지 않는 추가 방어입니다.[1]
- ✓서버의 외부 통신을 필요한 대상으로 제한하고 loopback, link-local, private 범위, 클라우드 메타데이터를 네트워크 계층에서 차단하세요. 외부 요청 코드에서 모든 리디렉션과 실제 연결 peer를 다시 검증하세요. 운영 환경에서는
MLFLOW_WEBHOOK_ALLOW_PRIVATE_IPS를 비활성화하고, 격리된 개발 환경에서 꼭 필요할 때만 활성화하세요.[2], [7] - ✓CI 회귀 테스트를 추가해 직접 비공개 주소, 리디렉션 대상, DNS rebinding, HTTP/HTTPS 풀, 프록시 경로가 요청 데이터를 보내기 전에 차단되는지 확인하고, 사설 IP 허용 예외도 별도로 테스트하세요.[2], [7]