← Back to Feed CACHED · 2026-10-07 20:37:53 · CACHE_KEY CVE-2026-64849
CVE-2026-64849 · CWE-918 · Disclosed 2026-08-17

MLflow Webhook Delivery Unauthenticated Full-Read SSRF via HTTP Redirect Bypass

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

Your ML experiment tracker just became a free proxy into your cloud metadata service, no password required

CVE-2026-64849 is an unauthenticated Server-Side Request Forgery (SSRF) in MLflow's model-registry webhook delivery system. The POST /api/2.0/mlflow/webhooks/{id}/test endpoint calls _validate_webhook_url() on the caller-supplied URL, but when the HTTP client follows a 302 redirect, it re-resolves the redirect target without re-checking the SSRF allow-list. An attacker sends a webhook URL pointing to their own HTTPS server, which passes validation and then returns a 302 to http://169.254.169.254/latest/meta-data/iam/security-credentials/…. MLflow dutifully follows the redirect and echoes the IMDS response body — containing live AWS (or GCP/Azure) temporary credentials — straight back to the attacker. A second variant using 307/308 redirects preserves the POST method and body, letting attackers blind-write into internal management APIs (Docker daemon /stop, Elasticsearch /_close, Spring Boot Actuator /shutdown). Every MLflow release below 3.15.0 is affected, including the entire 2.x line which is de facto end-of-life and will never receive a backport.

The vendor-assigned CVSS 9.3 CRITICAL is accurate and, if anything, conservative. The endpoint requires zero authentication, has zero user interaction, and the scope change (S:C) reflects the fact that the SSRF breaks out of MLflow's trust boundary into cloud infrastructure. With 30,000+ internet-exposed instances (per Shodan/Censys surveys from mid-2026), active exploitation logged within hours of disclosure, CISA KEV listing within 48 hours, and multiple public PoCs on GitHub, this is the rare vuln where the vendor severity number matches the operational urgency exactly. There is no meaningful friction anywhere in the chain.

"Unauthenticated SSRF in MLflow webhooks gives attackers a one-request path to your cloud credentials."
02 · The Attack Path

5 steps from start to impact.

STEP 01

Discover exposed MLflow Tracking Server

The attacker uses Shodan, Censys, or FOFA to search for the MLflow UI banner on port 5000 (default). MLflow ships with no authentication by default, and most cloud deployments keep it that way. As of mid-2026, over 31,000 instances were internet-exposed. No credentials are needed to interact with the API.
Conditions required:
  • MLflow Tracking Server reachable from the internet on default port 5000
Where this breaks in practice:
  • If the operator has enabled an auth plugin (e.g., mlflow-auth-basic) or placed the server behind a VPN/zero-trust gateway, this step fails
Detection/coverage: Shodan dork: title:"MLflow". Censys query: services.http.response.html_title:"MLflow". External attack surface management tools (Wiz, Orca, Prisma Cloud) flag exposed MLflow instances.
STEP 02

Create a webhook pointing to attacker-controlled HTTPS host

The attacker calls POST /api/2.0/mlflow/registered-models/create-webhook with a http_url_spec.url set to their own public HTTPS endpoint (e.g., https://evil.attacker.com/redir). The _validate_webhook_url() function resolves the hostname, confirms it is a public IP, and stores the webhook. This step passes validation cleanly because the attacker's host is public.
Conditions required:
  • Network access to the MLflow API
  • No authentication gate in front of the webhook API
Where this breaks in practice:
  • Organizations that restrict outbound HTTPS from the MLflow host to an allow-list of domains block the attacker's redirect server
STEP 03

Trigger the webhook test endpoint

The attacker calls POST /api/2.0/mlflow/webhooks/{id}/test. This unauthenticated endpoint synchronously fetches the stored webhook URL using requests.Session with redirect-following enabled. The attacker's server returns a 302 Found with Location: http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name>. MLflow follows the redirect to the IMDS endpoint without re-validating the target IP against the SSRF allow-list.
Conditions required:
  • Webhook created in step 2
  • MLflow host can reach link-local IMDS (169.254.169.254)
Where this breaks in practice:
  • If IMDSv2 (hop-limit=1) is enforced on the EC2 instance, the PUT-based token exchange fails from within the application layer — but IMDSv1 is still the default for many deployments
  • GCP and Azure metadata endpoints have their own header requirements, though the 307/308 variant can supply custom headers
Detection/coverage: WAF rules looking for 169.254.169.254 in redirect chains. Cloud provider IMDS access logs (AWS CloudTrail ec2:DescribeInstances won't catch this, but VPC Flow Logs to 169.254.169.254 can).
STEP 04

Exfiltrate cloud credentials from IMDS response

The /test endpoint returns the upstream response_status and response_body directly to the caller. The response body contains temporary IAM credentials (AccessKeyId, SecretAccessKey, Token) valid for up to 6 hours. The attacker now possesses valid cloud credentials scoped to whatever IAM role is attached to the MLflow host — typically a broad role with access to S3/GCS buckets containing training data, model artifacts, and secrets.
Conditions required:
  • IMDS is accessible from the MLflow process (not blocked by host firewall or IMDSv2 hop-limit)
Where this breaks in practice:
  • If the IAM role is tightly scoped (least-privilege), blast radius is limited — but ML workload roles are notoriously over-permissioned
Detection/coverage: AWS GuardDuty UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS fires when IMDS credentials are used from an IP outside the VPC.
STEP 05

Cloud account takeover and lateral movement

With stolen credentials, the attacker enumerates cloud resources (aws sts get-caller-identity, aws s3 ls, aws secretsmanager list-secrets). They pivot to production databases, exfiltrate regulated data, poison ML model artifacts in S3, or create new IAM users for persistence. The blast radius is cloud-tenant-scale. In the 307/308 blind-write variant, the attacker can additionally POST shutdown commands to internal services (Docker, Elasticsearch, Spring Boot Actuator) causing denial of service.
Conditions required:
  • Valid cloud credentials from step 4
  • Cloud resources accessible to the stolen role
Where this breaks in practice:
  • Cloud-native detection (GuardDuty, Azure Defender, GCP SCC) may flag anomalous API calls from unusual IPs
  • SCPs or VPC endpoint policies may limit credential use outside the account
Detection/coverage: AWS GuardDuty, Azure Defender for Cloud, GCP Security Command Center. SIEM correlation on credential use from non-VPC source IPs.
03 · Compensating Control

1
CRITICAL 9.3→LOW 3.1
SEVERITY REDUCED
Block external access to MLflow Tracking Server immediately — Place MLflow behind a VPN, zero-trust proxy (e.g., Cloudflare Access, Zscaler Private Access), or restrict security group / firewall rules to internal-only. This eliminates the unauthenticated remote attack vector entirely. Per the noisgate mitigation SLA for CRITICAL, deploy within 3 days. Given active exploitation and KEV listing, treat as immediate — within hours.
2
CRITICAL 9.3→HIGH 7.2
SEVERITY REDUCED
Enforce IMDSv2 with hop-limit=1 on all EC2 instances running MLflow — Set HttpTokens=required and HttpPutResponseHopLimit=1 on the instance metadata service. This forces token-based IMDS access that the SSRF redirect cannot satisfy, blocking the primary credential-theft path on AWS. Does NOT protect against SSRF to other internal services. Deploy within 3 days.
3
CRITICAL 9.3→MEDIUM 5.5
SEVERITY REDUCED
Enable MLflow authentication plugin — Deploy mlflow-auth-basic or integrate with your IdP (OIDC/SAML). This gates the webhook API behind credentials, preventing unauthenticated SSRF. Note: this requires MLflow 2.5+ and does not protect against authenticated insiders. Deploy within 3 days.
4
CRITICAL 9.3→MEDIUM 4.8
SEVERITY REDUCED
Restrict outbound network from MLflow host — Apply egress firewall rules (security group, iptables, or service mesh policy) allowing the MLflow process to reach only approved webhook destinations. Block all outbound to 169.254.169.254, metadata.google.internal, and RFC 1918 ranges. This breaks the redirect chain even if the SSRF fires. Deploy within 3 days.
5
CRITICAL 9.3→IGNORE 0.0
SEVERITY REDUCED
Upgrade to MLflow 3.15.0+ (3.16.0 recommended) — The definitive fix. Version 3.15.0 implements SSRFProtectedHTTPAdapter validating peer IP at connect() time. Version 3.16.0 closes IPv6-transition bypasses. For MLflow 2.x users, there is no backport — you must migrate to 3.x. Per the noisgate remediation SLA for CRITICAL, complete within 90 days. Given KEV listing, federal agencies must remediate by September 2, 2026.
What doesn't work
  • IMDSv2 alone is insufficient. It blocks the AWS IMDS credential-theft path but the SSRF can still reach any internal HTTP service — Redis, Elasticsearch, Docker daemon, internal APIs. The vulnerability is an unrestricted SSRF, not just an IMDS reader.
  • TLS termination at a reverse proxy does not help. The SSRF originates from the MLflow server process itself, not from the client connection. Terminating TLS at nginx/ALB does nothing to prevent the server-side redirect following.
  • Network segmentation without egress filtering is incomplete. Placing MLflow in a dedicated subnet stops lateral network access but does not block IMDS (which is link-local on the same host) or other services in the same subnet.
  • Upgrading to MLflow 3.10.0–3.14.x does not help. The SSRF guard added in 3.10.0 is the exact code being bypassed by this vulnerability. Only 3.15.0+ contains the fix.
04 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationConfirmed and active. WatchTowr detected indiscriminate scanning for exposed MLflow instances within hours of CVE assignment on August 17, 2026. Multiple financially motivated actors and initial access brokers are running automated exploit chains via Tor exit nodes and cloud proxies. CISA added CVE-2026-64849 to the KEV catalog on August 19, 2026 with a federal remediation deadline of September 2, 2026.
Proof-of-Concept AvailabilityMultiple public PoCs. GitHub repos BiuTrap/CVE-2026-64849 and codeb0ssx/CVE-2026-64849-PoC provide working exploit code. The vendor GHSA itself includes a step-by-step PoC workflow. Exploit complexity is trivial — a single curl command.
EPSS Score0.09839 (9.8%) — above the 95th percentile of all scored CVEs. This places it in the highest-risk band for predicted exploitation probability within 30 days.
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N → 9.3 Critical. Network-attackable, no auth, no user interaction, scope change into cloud infra, high confidentiality impact (credential theft), low integrity impact (blind-write variant via 307/308).
Affected VersionsAll MLflow releases below 3.15.0, including the entire 1.x and 2.x lines. The SSRF guard introduced in 3.10.0 is bypassable. MLflow 2.x (final release 2.22.5, May 12, 2026) is de facto end-of-life and will never receive a backport — migration to 3.15.0+ is the only path.
Fixed VersionMLflow 3.15.0 (released July 31, 2026, commit ba94952247, PR #24258). Implements SSRFProtectedHTTPAdapter that validates peer IP at connect() time, covering all redirect targets and DNS rebinding. A follow-up in 3.16.0 (PR #25568) closes IPv6-transition bypasses.
Internet Exposure~31,773 exposed MLflow Tracking Servers indexed by Shodan/Censys as of June 2026, up 173% from 11,620 a month earlier. MLflow ships with no authentication by default on port 5000. Attackers are using Shodan and Censys to enumerate targets at scale.
Reporting ResearchersOriginally reported by freeman-bb (private disclosure, June 12, 2026). Independently discovered by AUTHENSOR (public issue #24179, June 26, 2026). Additional finders: y011d4, ibondarenko1, h1-mrz, th3cyb3rc0p. Post-disclosure exploitation tracking by WatchTowr.
CERT AdvisoriesNational CERT advisories issued by Germany (BSI), Netherlands (NCSC-NL), Slovakia (SK-CERT), Canada (CCCS), Japan (JPCERT), South Korea (KrCERT), and India (CERT-In). Greenbone OpenVAS Enterprise Feed includes remote detection.
MLflow Install Base27,000+ GitHub stars, 60+ million monthly PyPI downloads. Primarily deployed as a centralized ML experiment tracking and model registry server in cloud environments (AWS, GCP, Azure). Cloud-hosted instances with IMDS access are the primary exploitation target.

Sources.

  1. GitHub Security Advisory GHSA-7gwp-5pfp-969j
  2. Greenbone — MLflow SSRF Vulnerability CVE-2026-64849 Exploited
  3. SecurityWeek — MLflow Vulnerability Exploited for Cloud Credential Theft
  4. The Hacker News — Attackers Exploit MLflow SSRF Flaw to Steal Cloud Credentials
  5. MLflow Fix PR #24258 — DNS-rebinding SSRF bypass in webhook delivery
  6. Rescana — Active Exploitation of MLflow SSRF Enables Cloud Credential Theft
  7. Decryption Digest — MLflow SSRF CVE-2026-64849 Cloud Credential Theft Explained
  8. EndOfLife.ai — MLflow 2.x EOL and CVE-2026-64849
05 · The Call

Final Verdict
= UNCHANGED to CRITICAL (9.3/10)

Why this verdict

  • Zero friction on attacker position: The vulnerable endpoint (POST /api/2.0/mlflow/webhooks/{id}/test) requires no authentication, no special privileges, and no user interaction. This is the lowest possible bar — any host that can reach the MLflow port can exploit it.
  • Massive internet exposure eliminates reachability friction: Over 31,000 MLflow instances are internet-exposed with no auth gate. MLflow ships unauthenticated by default. This is not a theoretical exposure — attackers are actively scanning and exploiting these instances at scale.
  • Trivial exploit with weaponized PoCs: Multiple public PoCs exist, the vendor advisory includes a step-by-step walkthrough, and exploitation was automated within hours of disclosure. The entire chain is a single HTTP POST with a redirect — no memory corruption, no race condition, no complex tooling.
  • Role multiplier: MLflow occupies the CI/CD + artifact stores tier from the high-value-role catalog. In production deployments, MLflow Tracking Server is the centralized model registry and experiment orchestration layer — analogous to an artifact repository. (a) *Dev sandbox*: local mlflow ui on a laptop — SSRF impact is limited to localhost services, LOW. (b) *Typical role*: team tracking server in a cloud VPC — SSRF reaches IMDS, credential theft gives access to the team's cloud resources, HIGH. (c) *High-value role*: production ML pipeline hub with IAM roles granting access to training data lakes, model artifact buckets, secrets managers, and compute orchestration — credential theft yields cloud-tenant-scale blast radius including supply-chain poisoning of ML models. A significant share of MLflow's 60M+ monthly downloads serve production ML platforms in cloud environments. The high-value role outcome (cloud account takeover, model supply-chain poisoning) with ≥10% of installs in that role floors the verdict at CRITICAL.
  • KEV-listed with active exploitation: CISA added this to KEV within 48 hours of disclosure. WatchTowr confirmed automated exploitation campaigns by financially motivated actors. This is not a theoretical risk — it is an active incident.

Why not higher?

The score is already at 9.3 CRITICAL, near the ceiling for a confidentiality-focused vulnerability without direct code execution. A 10.0 would require high integrity AND availability impact (e.g., full RCE). The blind-write variant (307/308) adds limited integrity impact but does not achieve arbitrary code execution on the MLflow host itself.

Why not lower?

Every friction-reducing factor aligns in the attacker's favor: unauthenticated, internet-exposed by default, trivial to exploit, actively exploited, public PoCs, KEV-listed, and the blast radius in cloud deployments is tenant-scale credential theft. The only theoretical friction (auth plugins, IMDSv2, VPN) represents the exception rather than the norm across the 31,000+ exposed instances. Dropping below CRITICAL would require ignoring confirmed active exploitation and the default-unauthenticated deployment model.

06 · Verification

Crowdsourced verification payload.

Run this script on each host running an MLflow Tracking Server (or against your Python environment where mlflow is installed). Requires read access to the pip or mlflow binary — no root needed. Invoke with: bash check_cve_2026_64849.sh

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_cve_2026_64849.sh
# Checks whether the installed MLflow version is vulnerable to CVE-2026-64849
# (unauthenticated SSRF via webhook redirect bypass)
# Fixed in: MLflow 3.15.0
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN

set -euo pipefail

FIXED_MAJOR=3
FIXED_MINOR=15
FIXED_PATCH=0

# Try to get MLflow version
if command -v mlflow &>/dev/null; then
  VERSION=$(mlflow --version 2>/dev/null | grep -oP '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
elif command -v pip &>/dev/null; then
  VERSION=$(pip show mlflow 2>/dev/null | grep -i '^Version:' | grep -oP '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
elif command -v pip3 &>/dev/null; then
  VERSION=$(pip3 show mlflow 2>/dev/null | grep -i '^Version:' | grep -oP '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
else
  echo "UNKNOWN - mlflow and pip not found in PATH"
  exit 2
fi

if [ -z "${VERSION:-}" ]; then
  echo "UNKNOWN - could not determine MLflow version"
  exit 2
fi

IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"

echo "Detected MLflow version: $VERSION"
echo "Fixed version: ${FIXED_MAJOR}.${FIXED_MINOR}.${FIXED_PATCH}"

if [ "$MAJOR" -gt "$FIXED_MAJOR" ]; then
  echo "PATCHED - MLflow $VERSION >= 3.15.0"
  exit 0
elif [ "$MAJOR" -eq "$FIXED_MAJOR" ] && [ "$MINOR" -gt "$FIXED_MINOR" ]; then
  echo "PATCHED - MLflow $VERSION >= 3.15.0"
  exit 0
elif [ "$MAJOR" -eq "$FIXED_MAJOR" ] && [ "$MINOR" -eq "$FIXED_MINOR" ] && [ "$PATCH" -ge "$FIXED_PATCH" ]; then
  echo "PATCHED - MLflow $VERSION >= 3.15.0"
  exit 0
else
  echo "VULNERABLE - MLflow $VERSION < 3.15.0 is affected by CVE-2026-64849"
  if [ "$MAJOR" -lt 3 ]; then
    echo "WARNING: MLflow ${MAJOR}.x is end-of-life. No backport exists. Migration to 3.15.0+ required."
  fi
  exit 1
fi