It's like leaving a signed blank check on the front desk — anyone walking by can cash it at any bank in the building
CVE-2026-64849 is an unauthenticated Server-Side Request Forgery (SSRF) in MLflow's webhook test endpoint (POST /api/2.0/mlflow/webhooks/{id}/test). The _validate_webhook_url() function in mlflow/utils/validation.py resolves the supplied hostname and blocks RFC-1918 / link-local addresses, but mlflow/webhooks/delivery.py follows HTTP 3xx redirects without re-validating the destination. An attacker hosts a public URL that passes the initial check, then issues a redirect to http://169.254.169.254/latest/meta-data/iam/security-credentials/ or any internal service. The full response body is reflected back to the caller. All MLflow versions before 3.15.0 are affected; no authentication or special configuration is required.
The vendor's CRITICAL 9.3 score is accurate and, if anything, generous only in not accounting for the active exploitation. The CVSS vector (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N) correctly reflects the unauthenticated, network-reachable, cross-scope read. Because MLflow tracking servers are routinely deployed on cloud VMs and containers with attached IAM roles, the practical impact of this full-read SSRF is cloud credential theft at scale — not a theoretical internal port scan. watchTowr's honeypot network confirmed exploitation within hours of disclosure.
5 steps from start to impact.
Discover exposed MLflow tracking server
0.0.0.0:5000 and returns distinctive response headers and HTML titles. Shodan sweeps in June 2026 counted MLflow exposure up 173% year-over-year, with thousands of instances publicly reachable.- MLflow tracking server reachable from the internet or attacker-controlled network segment
- Organizations behind VPN/Zero-Trust overlays are not reachable
- Some cloud deployments use ALB with authentication middleware in front
http.title:"MLflow" or http.favicon.hash matching MLflow. GreyNoise tags may appear for MLflow scanning activity.Prepare redirect server
302 Found redirect to the target internal URL (e.g., http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name>). The redirect target is chosen based on the suspected cloud provider. This is trivial — a single Python one-liner or Nginx rewrite rule suffices. DNS rebinding is an alternative bypass if redirects are blocked.- Attacker controls a public HTTP endpoint
- None — trivial to set up
Send unauthenticated webhook test request
POST /api/2.0/mlflow/webhooks/{id}/test with the redirect URL as the webhook target. _validate_webhook_url() resolves the attacker's public hostname, sees a public IP, and allows it. The request proceeds to delivery.py, which follows the 302 redirect to the internal/metadata endpoint without re-validation.- No authentication required
- Webhook test endpoint must be enabled (default)
- IMDSv2 (AWS) requires a PUT with hop-limit token — MLflow's GET/redirect flow cannot satisfy this, blocking credential retrieval on IMDSv2-enforced instances
- GCP and Azure metadata endpoints require specific headers (
Metadata-Flavor: Google,Metadata: true) that may not be forwarded through the redirect
169.254.169.254 in request bodies. IDS signatures for SSRF payloads in POST parameters.Receive reflected response with secrets
response_status, response_body). On AWS IMDSv1 instances, this contains temporary IAM credentials (AccessKeyId, SecretAccessKey, Token). On other cloud providers, service account tokens or managed identity tokens may be returned, depending on metadata header requirements.- Target internal service responds to the redirected request
- Cloud metadata service uses IMDSv1 or does not enforce request headers
- IMDSv2 enforcement blocks this path on AWS
- Network-level metadata firewalls (e.g., GKE Workload Identity, AWS instance metadata firewall) block access
AssumeRole or token generation events from the MLflow instance's IAM role.Lateral movement with stolen credentials
- Stolen credentials have useful permissions
- Credentials have not expired
- Least-privilege IAM roles limit blast radius
- AWS GuardDuty / GCP Security Command Center may flag credential use from anomalous IPs
UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS. Cloud SIEM correlation on credential use from non-VPC source IPs.The supporting signals.
| In-the-Wild Exploitation | Active. watchTowr Intel's Attacker Eye honeypot network detected exploitation attempts within hours of CVE assignment on 2026-08-18. Threat actors are scanning for exposed MLflow instances to steal cloud credentials. Not yet on CISA KEV. |
|---|---|
| Proof-of-Concept | Trivially reproducible. No named PoC repo yet, but the attack requires only a public redirect server and a single curl command. The CVE-2026-PoCs collection on GitHub is tracking 2026 disclosures. Full technical write-ups published by TheHackerWire and CyberPress. |
| EPSS Score | 0.00349 (low percentile) — EPSS has not yet caught up to the observed in-the-wild activity, likely due to the 2-day-old disclosure window. |
| KEV Status | Not listed as of 2026-08-19. Given active exploitation, KEV addition is plausible within days. |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N — Unauthenticated network attack, low complexity, no user interaction, scope change (crosses from MLflow to internal network/cloud), high confidentiality impact, low integrity impact (webhook state manipulation). |
| Affected Versions | All MLflow releases < 3.15.0. This includes the entire 2.x line and 3.0.0 through 3.14.x. |
| Fixed Version | MLflow 3.15.0 (commit ba94952, PR #24258). Fix pins the resolved IP before following redirects. |
| Exposure Data | Shodan/ARIAscout June 2026 sweep: MLflow internet exposure up 173% YoY. Thousands of instances publicly discoverable. MLflow default config binds to 0.0.0.0:5000 with no authentication. |
| Disclosure Date | 2026-08-17 (advisory published). Exploitation observed by 2026-08-18. |
| Reporter | Credited to GitHub_M per GHSA-7gwp-5pfp-969j. Issue #24179. |
noisgate verdict.
The decisive factor is active in-the-wild exploitation of an unauthenticated, zero-interaction SSRF that yields cloud credentials from a component whose internet exposure is growing 173% year-over-year. MLflow's canonical deployment role as ML pipeline infrastructure — CI/CD-adjacent with over-provisioned cloud IAM — means successful exploitation delivers credential theft that cascades to data stores, model registries, and cloud control plane.
Why this verdict
- Zero authentication barrier: The webhook test endpoint requires no credentials, no API key, no session — any network-reachable attacker can fire the exploit with a single POST request. This is the widest possible attack surface.
- Active exploitation confirmed: watchTowr honeypots detected scanning and exploitation within hours of disclosure. This is not theoretical; it is happening now against production MLflow deployments.
- Role multiplier: MLflow tracking servers are CI/CD-adjacent ML infrastructure. They canonically run on cloud VMs/containers with IAM roles granting access to S3/GCS training data, model artifact stores, Secrets Manager, and container registries. ≥50% of MLflow deployments occupy this high-value role by definition. A successful credential theft cascades from host-level to cloud-account-level, enabling data exfiltration and potential supply-chain compromise via model artifact tampering. The blast radius is cloud-account-scale, setting a CRITICAL floor.
- Massive and growing attack surface: Shodan data shows MLflow internet exposure up 173% YoY with thousands of instances publicly reachable, many running default configs with no authentication layer.
- Trivial exploit complexity: The attack requires only a public HTTP redirect server and a single curl command. No memory corruption, no race condition, no chain of bugs — just one request.
Why not higher?
The score is already at 9.3 CRITICAL, which is appropriate. A 10.0 would require arbitrary code execution with no mitigating factors. This is a read-only SSRF (C:H/I:L/A:N), not RCE, and cloud metadata protections (IMDSv2, GKE Workload Identity) do provide a meaningful partial mitigation on hardened deployments.
Why not lower?
Downgrading would require ignoring three compounding factors: (1) active exploitation in the wild, (2) zero authentication requirements on a network-exposed service, and (3) the CI/CD-adjacent role multiplier where credential theft cascades to cloud-account-level compromise. The IMDSv2 friction point only applies to AWS deployments that have enforced it — many have not, and GCP/Azure metadata protections vary. The growing internet exposure surface eliminates any 'niche deployment' argument.
What to do — in priority order.
- Enforce IMDSv2 on all AWS instances running MLflow — IMDSv2 requires a PUT request with a hop-limit-1 token before credential retrieval. MLflow's redirect-based SSRF cannot satisfy this, completely blocking the credential-theft path on AWS. Deploy within 3 days per noisgate mitigation SLA for CRITICAL findings. Use
aws ec2 modify-instance-metadata-options --instance-id <id> --http-tokens required --http-put-response-hop-limit 1. - Place MLflow behind an authenticating reverse proxy or VPN — MLflow has no built-in authentication. Deploy an OAuth2 proxy, Cloudflare Access, or restrict access to VPN/Zero-Trust overlay within 3 days. This eliminates the unauthenticated attack vector entirely.
- Deploy network-level metadata firewall — On GKE, enable Workload Identity. On EKS, use IRSA instead of instance profiles. On bare VMs, use iptables to block the MLflow process from reaching
169.254.169.254. Deploy within 3 days. - Apply WAF rules blocking SSRF payloads — Configure WAF to inspect POST body parameters for
169.254.169.254,127.0.0.1, and RFC-1918 ranges in webhook URL fields. This provides defense-in-depth but is bypassable via DNS rebinding. - Upgrade to MLflow 3.15.0 — The definitive fix. The patched version re-validates redirect destinations against the same IP blocklist. Plan upgrade within the 90-day noisgate remediation SLA, but given active exploitation, prioritize immediate deployment.
- Network segmentation alone — MLflow needs outbound HTTP to deliver webhooks by design; blocking all outbound from MLflow breaks functionality. The SSRF exploits *intended* outbound connectivity.
- Application-layer rate limiting — The exploit succeeds on a single request; rate limiting does not prevent exploitation, only slows scanning.
- Disabling webhooks via config — MLflow does not have a clean feature flag to disable the webhook test endpoint without code modification; the
/testroute is always registered.
Crowdsourced verification payload.
Run this script on each host running MLflow (or from an auditor workstation with pip access to the MLflow environment). No special privileges required. Invoke: bash check_cve_2026_64849.sh or bash check_cve_2026_64849.sh /path/to/python
#!/usr/bin/env bash
# CVE-2026-64849 checker — MLflow unauthenticated SSRF in webhook delivery
# Usage: bash check_cve_2026_64849.sh [/path/to/python]
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN
set -euo pipefail
PYTHON="${1:-python3}"
if ! command -v "$PYTHON" &>/dev/null; then
echo "UNKNOWN — python3 not found"
exit 2
fi
VERSION=$($PYTHON -c "import mlflow; print(mlflow.__version__)" 2>/dev/null) || {
echo "UNKNOWN — mlflow is not installed in this Python environment"
exit 2
}
echo "Detected MLflow version: $VERSION"
# Compare versions using Python for reliability
RESULT=$($PYTHON -c "
from packaging.version import Version
import sys
try:
v = Version('$VERSION')
if v >= Version('3.15.0'):
print('PATCHED')
else:
print('VULNERABLE')
except Exception as e:
print('UNKNOWN')
" 2>/dev/null) || {
# Fallback if packaging module unavailable
RESULT=$($PYTHON -c "
import sys
parts = '$VERSION'.split('.')
try:
major, minor = int(parts[0]), int(parts[1])
if (major > 3) or (major == 3 and minor >= 15):
print('PATCHED')
else:
print('VULNERABLE')
except:
print('UNKNOWN')
")
}
echo "$RESULT — CVE-2026-64849 (MLflow SSRF in webhook delivery)"
case "$RESULT" in
PATCHED) exit 0 ;;
VULNERABLE) exit 1 ;;
*) exit 2 ;;
esacIf you remember one thing.
Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.