Skip to content

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.

REPRO-2026-00335 mlflow/mlflow · PyPI SSRF Aug 23, 2026 CVE entry .txt
Severity
CRITICAL
Confidence
HIGH
Reproduced in
41m 3s
Tool calls
304
Spend
$10.00
01 · Overview

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).

02 · Severity & CVSS

CVE-2026-64849 Severity

CVE-2026-64849 is rated critical severity.

CRITICAL threat level
Weakness CWE-918 Server-Side Request Forgery — Server-Side Request Forgery (SSRF)

Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.

03 · Affected Versions

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
or 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
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-64849

Server-side request forgery — reproduced
  • 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
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

Attack chain
  1. 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 506 events · 304 tool calls · 41 min
41 minDuration
304Tool calls
80Reasoning steps
506Events
10Dead-ends
Agent activity over 41 min
Policy
1
Support
29
Repro
255
Judge
49
Variant
167
Verify
1
0:0040:52

Root Cause and Exploit Chain for CVE-2026-64849

Versions: component: MLflow Tracking Server model-registry webhook API and mlflow/webhooks/delivery.py.

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-data after a validation-safe HTTPS redirect and returned PRUVA_INTERNAL_SSRF_CANARY_CVE_2026_64849 with success: true and response_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:

  1. The default mlflow server does not load the optional authentication plugin, so webhook creation and test routes are reachable without credentials.
  2. _validate_webhook_url validates the original webhook hostname and rejects its initially resolved non-public addresses, but the validated address is not bound to subsequent connections.
  3. In 3.13.0, _send_webhook_request uses a normal Requests session, and session.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

  1. Run bundle/repro/reproduction_steps.sh from any working directory. Docker, Git, curl, OpenSSL, and Python 3 are required.
  2. The script reads bundle/project_cache_context.json, reuses <project_cache_dir>/repo when 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)
  3. 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 at 172.30.49.10 and MLflow at 172.30.49.20.
  4. It launches the real default Tracking Server with SQLite and no auth plugin, waits for /health, creates a webhook through POST /api/2.0/mlflow/webhooks, and invokes POST /api/2.0/mlflow/webhooks/{id}/test without credentials.
  5. 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.
  6. 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.txt
    • bundle/logs/repro/vuln/test-response.json
  • Fixed API request/response:
    • bundle/logs/repro/fixed/test-wire.txt
    • bundle/logs/repro/fixed/test-response.json
  • Production service logs:
    • bundle/logs/repro/vuln/mlflow-service.log
    • bundle/logs/repro/fixed/mlflow-service.log
  • Redirect and private-service evidence:
    • bundle/logs/repro/vuln/redirector.log
    • bundle/logs/repro/fixed/redirector.log
    • bundle/logs/repro/final-canary.log
    • bundle/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.

Event 1/40
0:003:27
0:00
session startedgpt-5.6-sol · CVE-2026-64849 · REPRO-20
0:08
web search
0:11
0:25
0:25
0:26
0:27
web search
0:28
web search
0:45
0:45
0:46
web search
0:48
web search
0:49
web search
1:10
1:11
web search
1:30
1:33
3:02
3:02
3:02
3:02
3:02
extract_facts
no facts extracted
3:10
3:10
supportclaim_contract
3:19
3:20
3:20
3:20
3:27
3:27
08 · How to Fix

How to Fix CVE-2026-64849

Upgrade mlflow/mlflow · PyPI to mlflow==3.15.0 or later.

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-64849 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-64849

Is CVE-2026-64849 exploitable?

Yes. Pruva independently reproduced CVE-2026-64849 in mlflow/mlflow and verified the exploit fires end-to-end in a sandboxed environment. A runnable proof-of-concept script and the full agent transcript are on this page (reproduction REPRO-2026-00335).

How severe is CVE-2026-64849?

CVE-2026-64849 is rated critical severity.

What type of vulnerability is CVE-2026-64849?

CVE-2026-64849 is classified as CWE-918 Server-Side Request Forgery (Server-Side Request Forgery (SSRF)), a SSRF vulnerability.

Which versions of mlflow/mlflow are affected by CVE-2026-64849?

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. is affected by CVE-2026-64849.

Is there a fix for CVE-2026-64849?

Yes. CVE-2026-64849 is fixed in mlflow/mlflow mlflow==3.15.0. Upgrading to the fixed version remediates the issue.

How can I reproduce CVE-2026-64849?

Pruva provides a verified reproduction script on this page. Download it and run it inside an isolated environment such as a container or virtual machine — never against production. The reproduction was confirmed end-to-end by Pruva's automated agents.

Is the CVE-2026-64849 reproduction verified?

Yes. Pruva reproduced CVE-2026-64849 with high confidence in a sandboxed environment, capturing the full agent transcript and artifacts as evidence.
11 · References

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.