CVE-2026-64849: Verified Reproduction
CVE-2026-64849: MLflow unauthenticated full-read SSRF in webhook delivery via redirect-follow bypass of validate webhook url guard
CVE-2026-64849 is verified against mlflow/mlflow · PyPI. Affected versions: Official advisory range is mlflow < 3.15.0. The URL guard exists in 3.10.0 through 3.14.x and is bypassable by redirects or DNS rebinding; versions before 3.10.0 lack this guard and permit easier SSRF. Fixed in mlflow==3.15.0. Vulnerability class: SSRF. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00335.
What Is CVE-2026-64849?
CVE-2026-64849 is a critical-severity SSRF vulnerability affecting mlflow/mlflow Official advisory range is mlflow < 3.15.0. The URL guard exists in 3.10.0 through 3.14.x and is bypassable by redirects or DNS rebinding; versions before 3.10.0 lack this guard and permit easier SSRF.. Pruva has independently reproduced it and publishes a verified, runnable proof-of-concept (reproduction REPRO-2026-00335).
CVE-2026-64849 Severity
CVE-2026-64849 is rated critical severity.
Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.
Affected mlflow/mlflow Versions
mlflow/mlflow · PyPI versions Official advisory range is mlflow < 3.15.0. The URL guard exists in 3.10.0 through 3.14.x and is bypassable by redirects or DNS rebinding; versions before 3.10.0 lack this guard and permit easier SSRF. are affected.
How to Reproduce CVE-2026-64849
pruva-verify REPRO-2026-00335 curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00335/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-64849
- reached the target end-to-end
- full exploit chain demonstrated
- on the real production code path
- high confidence
- the upstream fix blocks the same trigger
Unauthenticated webhook URL https://93.184.216.34:8443/redirect whose HTTPS response redirects to private http://172.30.49.10:9000/latest-meta-data
- POST /api/2.0/mlflow/webhooks followed by POST /api/2.0/mlflow/webhooks/{id}/test on the default Tracking Server
How the agent worked
Root Cause and Exploit Chain for CVE-2026-64849
CVE-2026-64849 is confirmed in the real default MLflow Tracking Server API. MLflow 3.13.0 validates only the configured webhook URL, then its Requests session follows an HTTPS 302 redirect to an unvalidated private address. An unauthenticated caller can create that webhook and invoke POST /api/2.0/mlflow/webhooks/{id}/test; MLflow fetches the private resource and reflects its body. The same current-run procedure against MLflow 3.15.0 reaches the public HTTPS redirector but blocks the redirected connection before sending HTTP to the private service.
- Affected component: MLflow Tracking Server model-registry webhook API and
mlflow/webhooks/delivery.py. - Affected versions: Upstream advisory scope is MLflow versions before 3.15.0. This run directly proves 3.13.0 vulnerable and 3.15.0 fixed.
- Risk level and consequences: Critical, unauthenticated full-read SSRF. A remote caller can read HTTP resources reachable from the MLflow host, including private network and cloud metadata-style services, and receive those bytes in the API response.
Impact Parity
- Disclosed/claimed maximum impact: Unauthenticated full-read SSRF through the default remote API, with internal response data reflected to the caller.
- Reproduced impact from this run: The real MLflow 3.13.0 API fetched
http://172.30.49.10:9000/latest-meta-dataafter a validation-safe HTTPS redirect and returnedPRUVA_INTERNAL_SSRF_CANARY_CVE_2026_64849withsuccess: trueandresponse_status: 200. - Parity:
full - Not demonstrated: The 307/308 blind-write variant and DNS-rebinding variant were not needed for impact parity and were not tested here. No code execution or privilege escalation is claimed.
Root Cause
Three behaviors compose into the vulnerability:
- The default
mlflow serverdoes not load the optional authentication plugin, so webhook creation and test routes are reachable without credentials. _validate_webhook_urlvalidates the original webhook hostname and rejects its initially resolved non-public addresses, but the validated address is not bound to subsequent connections.- In 3.13.0,
_send_webhook_requestuses a normal Requests session, andsession.post(webhook.url, ...)follows redirects by default. The redirect target is not passed back through_validate_webhook_url, so a public HTTPS first hop can redirect to RFC1918, loopback, or link-local HTTP.
The fix is upstream commit ba949522477cbd5915aa55d29b0cfad7d5ddf939, “Fix DNS-rebinding SSRF bypass in webhook delivery (#24258).” It introduces SSRFProtectedHTTPAdapter, validates the peer address of every actual connection (including a redirect connection), and disables environment proxy handling for the webhook session. MLflow 3.15.0 contains this code. In the fixed control, the initial HTTPS endpoint was successfully contacted, but connecting to the redirected private peer produced SSRFProtectionError('Webhook connection blocked: 172.30.49.10 is not a public IP address...').
Reproduction Steps
- Run
bundle/repro/reproduction_steps.shfrom any working directory. Docker, Git, curl, OpenSSL, and Python 3 are required. - The script reads
bundle/project_cache_context.json, reuses<project_cache_dir>/repowhen prepared, verifies exact upstream tag SHAs and the fix hunk, and pulls official MLflow images:- vulnerable:
ghcr.io/mlflow/mlflow@sha256:b2136d49f882fdc9c48bdb95514a8a23804e8756524135c3f09f7a553a3ac58b(mlflow.__version__ == 3.13.0) - fixed:
ghcr.io/mlflow/mlflow@sha256:2fef60dd85b18b4a555325b861b50b43ede45b1cc67176aa711cc86ad446a6a3(mlflow.__version__ == 3.15.0)
- vulnerable:
- It creates two isolated Docker bridges: a public-class network containing a trusted HTTPS redirector at
93.184.216.34, and an RFC1918 network containing a canary at172.30.49.10and MLflow at172.30.49.20. - It launches the real default Tracking Server with SQLite and no auth plugin, waits for
/health, creates a webhook throughPOST /api/2.0/mlflow/webhooks, and invokesPOST /api/2.0/mlflow/webhooks/{id}/testwithout credentials. - It requires the 3.13.0 response to contain the private canary and requires the private service request count to increase. It then performs the identical procedure on 3.15.0, requires connection-time private-peer rejection, and requires the private service request count not to increase.
- It writes
bundle/repro/runtime_manifest.json, including SHA-256 closure over all immutable proof artifacts. Exit code 0 means the vulnerable/fixed behavioral delta was confirmed.
Evidence
- Runtime manifest:
bundle/repro/runtime_manifest.json - Pinned identities:
bundle/logs/repro/image-identities.txt - Vulnerable API request/response:
bundle/logs/repro/vuln/test-wire.txtbundle/logs/repro/vuln/test-response.json
- Fixed API request/response:
bundle/logs/repro/fixed/test-wire.txtbundle/logs/repro/fixed/test-response.json
- Production service logs:
bundle/logs/repro/vuln/mlflow-service.logbundle/logs/repro/fixed/mlflow-service.log
- Redirect and private-service evidence:
bundle/logs/repro/vuln/redirector.logbundle/logs/repro/fixed/redirector.logbundle/logs/repro/final-canary.logbundle/logs/repro/{vuln,fixed}/canary-private-requests-{before,after}.txt
Key vulnerable response:
{
"result": {
"success": true,
"response_status": 200,
"response_body": "PRUVA_INTERNAL_SSRF_CANARY_CVE_2026_64849\n"
}
}
The private request counter changed from 0 to 1 during the vulnerable test. The fixed response instead contains:
SSRFProtectionError('Webhook connection blocked: 172.30.49.10 is not a public IP address...')
The fixed counter remained 1 before and after its test, while the redirector log gained the second POST /redirect; this proves the negative control trusted and reached the first hop but sent no HTTP request to the private service. Both final consecutive executions of the script passed. No sanitizer or instrumentation was used.
Recommendations / Next Steps
- Upgrade to MLflow 3.15.0 or later.
- Validate the actual peer of every connection, including redirects and retries, rather than only resolving and checking the original URL.
- Ensure proxy configuration cannot bypass destination-peer validation, and preserve TLS verification against the original hostname.
- Require authentication and authorization for webhook creation, modification, and testing even when MLflow is deployed with otherwise default settings.
- Keep regression coverage for 301/302/303/307/308 redirects to loopback, RFC1918, link-local, IPv6-local, and mapped-address targets, plus DNS rebinding and proxy paths.
Additional Notes
The script is self-contained aside from standard tools and immutable remote images/repository objects that it fetches itself. It generates its own short-lived test CA and certificate, scopes trust to the MLflow test containers, creates fresh SQLite state, cleans child containers/networks on exit, and was verified twice consecutively in its final form. The public-class IP exists only inside an isolated Docker bridge; the private canary is a deterministic stand-in for an internal metadata endpoint, while all vulnerable logic and API behavior come from the unmodified official MLflow product image.
CVE-2026-64849 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
Artifacts and Evidence for CVE-2026-64849
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-64849
Upgrade mlflow/mlflow · PyPI to mlflow==3.15.0 or later.
FAQ: CVE-2026-64849
Is CVE-2026-64849 exploitable?
How severe is CVE-2026-64849?
What type of vulnerability is CVE-2026-64849?
Which versions of mlflow/mlflow are affected by CVE-2026-64849?
Is there a fix for CVE-2026-64849?
How can I reproduce CVE-2026-64849?
Is the CVE-2026-64849 reproduction verified?
References for CVE-2026-64849
Authoritative sources for CVE-2026-64849 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.