JSTGTECH
← Back to blog

MLflow SSRF (CVE-2026-64849): a validate-then-fetch bug

3 min read

CISA added CVE-2026-64849 to its Known Exploited Vulnerabilities catalog on August 19, 2026, giving federal agencies until September 2 to patch it (CISA KEV). The bug: an unauthenticated, CVSS 9.3 server-side request forgery in MLflow, the open-source ML experiment-tracking platform that ships in a lot of AI/ML pipelines running on EC2, GKE, and AKS. Researchers at watchTowr reported scanning for exposed MLflow servers starting within hours of the CVE being assigned (The Hacker News). What makes it worth a closer look isn’t just “another SSRF into cloud metadata” — it’s a textbook case of a security control that exists, looks correct, and still doesn’t work because of the gap between when you check something and when you use it.

Root cause

MLflow lets you register webhooks — URLs it calls when a model is registered, a run finishes, and so on — and it added an SSRF guard for exactly the obvious reason: don’t let a webhook URL point at 169.254.169.254 or 127.0.0.1. That guard, _validate_webhook_url (shipped in 3.10.0), resolves the hostname you supply and rejects it if the resolved IP is private, loopback, or link-local. Sound in isolation. The flaw is in the unauthenticated POST /api/2.0/mlflow/webhooks/{id}/test endpoint, which validates the URL and then, in a separate step, actually delivers a request to it — following HTTP redirects, with the validated IP never pinned to the connection that follows (GitHub advisory). That’s a classic time-of-check-to-time-of-use (TOCTOU) bug applied to network requests instead of a filesystem. Two ways to exploit the gap: host a public HTTPS endpoint that passes the resolve-and-check step, then have it respond 302 Location: http://169.254.169.254/latest/meta-data/iam/security-credentials/<role> — MLflow’s HTTP client happily follows the redirect to the real target without re-checking it. Or skip the redirect entirely and use DNS rebinding: register a hostname that resolves to a public IP at validation time and to 169.254.169.254 a few seconds later when the actual delivery request does its own, independent DNS lookup. Either way, the /test endpoint reflects the upstream response — status code and body — straight back to the caller, so this isn’t blind SSRF. It’s full-read: whatever comes back from the metadata service is handed to the attacker verbatim.

Blast radius

No authentication required, and MLflow’s tracking server is routinely deployed with far looser access controls than a production database, because it’s “just” an internal ML tool. That combination — internet- or VPC-reachable, unauthenticated by design in a lot of default configs, and now capable of full-read SSRF — means an attacker who finds an exposed instance can request /latest/meta-data/iam/security-credentials/<role-name> on AWS, the equivalent Azure IMDS token endpoint, or GCP’s metadata server, and read the response directly out of the webhook-test reply. From there it’s the same story as every other IMDS-credential-theft incident: the stolen credentials are indistinguishable from legitimate ones to anything downstream, and the damage ceiling is set by whatever IAM role, service account, or managed identity the MLflow host was running as. Given that ML training and tracking infrastructure often holds credentials with broad S3/GCS/blob storage access — that’s where the training data and model artifacts live — the instance role attached to an MLflow box is a plausible high-value target, not an afterthought.

Remediation

Upgrade to MLflow 3.15.0 or later, which fixes the actual bug: it pins the validated IP to the connection used for delivery instead of trusting a fresh DNS lookup or a redirect target (PR #24258). That’s the only complete fix — allowlisting webhook domains or blocking 169.254.169.254 at a WAF layer doesn’t address DNS rebinding, since the rebind happens after any URL-string-based check passes. Two things to do regardless of patch status. First, MLflow tracking servers should never be reachable from the public internet or from network paths a generic tenant workload can reach — treat it like any other internal admin service with a security boundary, not like a stateless API. Second, and this is the layer that actually caps the damage from any future SSRF in any internal tool, not just this one: enforce IMDSv2 (HttpTokens: required) on every instance, since its hop-limited, token-gated handshake breaks a plain redirect-or-rebind relay the same way it would have broken the Capital One SSRF path years earlier. Scope the instance role or service account narrowly, too — MLflow doesn’t need account-wide storage access to do its job, and a role limited to the specific artifact buckets it manages turns a successful SSRF into a contained incident instead of an account-wide one.

Related posts