Background
MLflow is an open-source platform for managing the machine learning lifecycle — experiment tracking, model packaging, a model registry, and deployment tooling. It is widely deployed by data science and MLOps teams, frequently on cloud infrastructure (AWS, GCP, Azure) where the Tracking Server sits alongside training pipelines, artifact stores, and cloud credentials scoped to the host’s IAM role or service account.
As part of its model registry, MLflow supports webhooks: HTTP callbacks fired when registry events occur (a new model version, a stage transition, and similar). To let operators verify a webhook is reachable before relying on it, MLflow exposes a test endpoint, POST /api/2.0/mlflow/webhooks/{id}/test, that triggers a delivery attempt and returns the upstream response status and body to the caller. In a default MLflow Tracking Server deployment, the model-registry webhooks API — including this test endpoint — is reachable without authentication.
CVE-2026-64849 turns that combination into a full-read SSRF primitive: an unauthenticated network client can point a webhook at attacker infrastructure, bounce the request to an internal address or a cloud metadata service, and receive the response back verbatim.
Technical Mechanism
CVE-2026-64849 is classified as CWE-918 (Server-Side Request Forgery). The root cause is a time-of-check to time-of-use (TOCTOU) gap between MLflow’s webhook URL validation and its webhook delivery logic.
The guard function and its gap. MLflow validates webhook URLs with _validate_webhook_url(), which resolves the target hostname via socket.getaddrinfo() and checks that the resolved IP is a public, globally-routable address (ip.is_global). Per the GitHub issue tracking the flaw (mlflow/mlflow#24179), the function discards the resolved IP once validation passes — it returns None, not the address it checked. When the webhook is actually delivered (or tested via the /test endpoint), MLflow’s _send_webhook_request() function performs a second, independent DNS resolution and HTTP request through a requests-based session (_create_webhook_session), which by default follows HTTP redirects.
That gap between “the address we checked” and “the address we actually connect to” is exploitable in two overlapping ways, both patched by the same fix:
- Redirect bypass. An attacker registers or tests a webhook pointing at a public HTTPS endpoint they control. That URL passes
_validate_webhook_url()because it resolves to a public IP. When MLflow delivers the webhook, the attacker’s server responds with an HTTP 302 (or 307/308, preserving method and body) redirecting to an internal target —127.0.0.1, an internal service, or the cloud instance metadata address169.254.169.254. MLflow’s HTTP client follows the redirect without re-validating that the new destination is public, and the internal service’s response is proxied back through the/testendpoint’s status and body. - DNS rebinding. An attacker controls a DNS record that resolves to a public IP during MLflow’s validation lookup, then re-resolves to a private or link-local IP by the time the actual request fires. Because validation and delivery perform separate
getaddrinfo()calls, this timing gap achieves the same outcome as the redirect path without relying on an HTTP 3xx response.
Because the /test endpoint requires no authentication on a default deployment and reflects the upstream response_status and response_body directly to the caller, either bypass gives an attacker a blind or full-read SSRF proxy through the MLflow Tracking Server, reachable from any network position that can send it HTTP requests. Using 307/308 redirects that preserve the POST method and body, the same primitive can be used for “blind write” requests against unauthenticated internal APIs (Docker daemon sockets exposed over HTTP, Spring Boot Actuator endpoints, and similar), not just metadata reads.
The fix, merged in mlflow/mlflow#24258, replaces resolve-then-request validation with connection-time enforcement: a new SSRFProtectedHTTPAdapter (in mlflow/webhooks/ssrf.py) validates the peer IP of the actual TCP socket immediately after connection, before the TLS handshake or any HTTP data is exchanged, and rejects the connection if that IP isn’t public. Because no second DNS resolution happens between the check and the connection, both the redirect and DNS-rebinding variants are closed by the same change.
Real-World Exploitation Evidence
CVE-2026-64849 was assigned and publicly disclosed on August 17, 2026, alongside the MLflow 3.15.0 release and GitHub security advisory GHSA-7gwp-5pfp-969j. Exploitation followed almost immediately: watchTowr Labs, via its Attacker Eye honeypot network, reported observing attackers targeting cloud-hosted MLflow systems within hours of the CVE being assigned. watchTowr’s Yordan Ganchev described the activity as attackers “abusing this vulnerability to target cloud-hosted MLflow systems in an attempt to extract credentials and secrets from well-known internal IP addresses and services” — i.e., systematic probing of cloud metadata endpoints and common internal service ports through compromised MLflow instances, consistent with opportunistic, internet-wide scanning rather than a targeted campaign.
CISA added CVE-2026-64849 to its Known Exploited Vulnerabilities (KEV) catalog, confirming active exploitation and setting a remediation deadline of September 2, 2026, for federal civilian agencies under BOD 26-04. No named threat actor or intrusion set has been publicly attributed to the activity at time of writing; coverage from watchTowr, The Hacker News, and others describes broad, automated scanning for exposed MLflow Tracking Servers rather than a single attributed operator.
The speed of exploitation — hours, not days — reflects both the simplicity of the attack (a single unauthenticated HTTP request chain) and the value of the target: MLflow Tracking Servers on cloud infrastructure routinely have IAM roles or service account credentials attached, making them attractive proxies for cloud credential theft.
Impact Assessment
A successful exploit gives an unauthenticated attacker a read (and, via 307/308 redirects, a write) primitive against any network location reachable from the MLflow Tracking Server host:
- Cloud credential theft: Reaching
169.254.169.254(AWS IMDS, and the equivalent metadata address on GCP/Azure) can return temporary IAM role credentials or service account tokens, which the attacker can then use directly against cloud APIs — no further exploitation of MLflow needed. - Internal network reconnaissance and access: The same primitive reaches any internal service the MLflow host can route to — internal APIs, admin panels, databases, or orchestration control planes (Docker daemon HTTP sockets, Kubernetes API servers, Spring Boot Actuators) — with responses reflected back to the attacker.
- Blind write to internal services: Because the webhook delivery mechanism preserves HTTP method and body across 307/308 redirects, an attacker can issue POST requests to internal endpoints that accept unauthenticated writes, not just read data.
- No pre-existing access required: Exploitation needs only network reachability to the MLflow Tracking Server’s webhook API — no valid MLflow credentials, since the
/testendpoint and webhook registration are unauthenticated by default.
Organizations running MLflow on cloud infrastructure with attached IAM roles face the most severe exposure, since compromised metadata-service credentials frequently grant access well beyond the ML infrastructure itself — into artifact storage buckets, other cloud services, and, depending on role scope, broader account resources.
Affected Versions
| Product | Affected Versions | Fixed Version |
|---|---|---|
| MLflow (Tracking Server / model registry webhooks) | All versions before 3.15.0 | 3.15.0 |
Remediation Steps
-
Upgrade immediately to MLflow 3.15.0 or later. The patch enforces peer-IP validation at TCP connection time via
SSRFProtectedHTTPAdapter, closing both the redirect-based and DNS-rebinding bypass paths in a single fix (mlflow/mlflow#24258, commitba949522477cbd5915aa55d29b0cfad7d5ddf939). -
Require authentication on the Tracking Server. MLflow does not enable authentication by default. Deploy MLflow behind an authenticating reverse proxy or enable MLflow’s built-in auth (
mlflow server --app-name basic-author equivalent) so the webhook API is not reachable by anonymous clients, as defense in depth even after patching. -
Restrict network exposure. The Tracking Server, and specifically the webhook management and test endpoints, should not be exposed to the public internet. Place it behind a VPN, internal network boundary, or allowlist.
-
Block metadata-service reachability where possible. On AWS, enforce IMDSv2 (which requires a session token obtained via a PUT request, mitigating simple GET-based SSRF against IMDS) and set a low hop limit on instances running MLflow. On GCP/Azure, apply equivalent metadata-server hardening. This does not fix the underlying SSRF but reduces the value of the primitive.
-
Rotate credentials accessible to the MLflow host’s IAM role or service account if the instance was internet-reachable and unpatched at any point after August 17, 2026, when public exploitation activity began.
-
Audit existing webhooks. Review registered webhook URLs in the model registry for any pointing at unexpected or attacker-controlled domains, which could indicate the flaw was already used to establish a persistent redirect target.
Detection Guidance
Application/API log indicators:
Review MLflow Tracking Server access logs for requests to the webhook API from unexpected source IPs, particularly:
POST /api/2.0/mlflow/webhooks HTTP/1.1
POST /api/2.0/mlflow/webhooks/{id}/test HTTP/1.1
Repeated /test calls against a small number of webhook IDs, or webhook registrations pointing at newly-registered or low-reputation domains, are indicators of probing.
Outbound network indicators:
Monitor outbound connections initiated by the MLflow host process for:
- Requests to
169.254.169.254(or the GCP/Azure metadata equivalents) originating from the MLflow service, which should never occur under normal operation - Outbound HTTP connections to internal RFC 1918 / link-local addresses that don’t correspond to known internal integrations
- DNS queries with unusually short TTLs or repeated re-resolution of the same hostname to different IPs in a short window (a rebinding indicator)
SIEM query (generic web/proxy log):
uri_path:"*/webhooks/*/test"
AND method:POST
| stats count by src_ip, dest_ip
| where dest_ip in ("169.254.169.254", "127.0.0.1") OR cidr_match(dest_ip, "10.0.0.0/8","172.16.0.0/12","192.168.0.0/16")
Any hit where a request through the MLflow webhook test path resolves to a metadata or internal address should be treated as confirmed exploitation, not a false positive.
Version check:
pip show mlflow | grep Version
Any result below 3.15.0 indicates an unpatched, vulnerable installation.
Timeline
| Date | Event |
|---|---|
| 2026-07-02 | Fix merged upstream (mlflow/mlflow#24258, commit ba94952) |
| 2026-08-17 | CVE-2026-64849 assigned; MLflow 3.15.0 released; GHSA-7gwp-5pfp-969j published |
| 2026-08-17 | watchTowr Intel (Attacker Eye honeypot network) observes active scanning and exploitation within hours of CVE assignment |
| 2026-08-19 | CISA adds CVE-2026-64849 to the Known Exploited Vulnerabilities catalog; federal remediation deadline set for 2026-09-02 |
| 2026-08-19 | This analysis published |
References
- MLflow PR #24258 — DNS-rebinding / connection-time SSRF fix
- MLflow Issue #24179 — DNS rebinding bypass of webhook SSRF protection
- NVD — CVE-2026-64849
- GitHub Security Advisory GHSA-7gwp-5pfp-969j
- IONIX Threat Center — CVE-2026-64849 Unauthenticated SSRF via Webhook Redirect Bypass
- GitLab Advisory Database — CVE-2026-64849
- The Hacker News — Attackers Exploit MLflow SSRF Flaw to Steal Cloud Credentials and Secrets
- Decipher — MLflow Bug Actively Exploited to Steal Credentials
- watchTowr Labs — exploitation observed via Attacker Eye
- CISA Known Exploited Vulnerabilities Catalog