MLflow CISA KEV: Is Your AI Stack In Pentest Scope?
The question "do we run MLflow?" should take four minutes to answer. Often it takes four hours and three teams. That gap, not the CVE, is the actual security problem.
Here is how it goes. Someone drops the CISA advisory into the security channel and the security lead asks whether the company runs MLflow. The platform team says they do not manage it. The data team thinks there is an instance but it belongs to the ML engineers. Three hours later an ML engineer confirms there is a tracking server, stood up eighteen months ago by a data scientist who has since moved teams, running in the data science account rather than production, with an instance role attached because training jobs need to read from the data lake and write to the artifact bucket. Someone checks the load balancer. It has been reachable from the public internet since March.
Nobody was negligent in that story. Every decision was reasonable when it was made. A research tool grew into production infrastructure without anyone declaring the moment it changed category.
That is the pattern worth attention. Organisations test the infrastructure they know about. Vulnerability management works off the asset register, pentest scope is derived from the asset list, patch SLAs apply to tracked systems. All of that machinery functions correctly, and all of it sits downstream of an inventory the MLflow server was never added to. The tracking server is the infrastructure nobody knew they had, holding credentials nobody remembered attaching, on a network path nobody reviewed.
What CISA Added on 19 August
On 19 August 2026, CISA added CVE-2026-64849 to the Known Exploited Vulnerabilities catalog. It is a server-side request forgery flaw (CWE-918) in the MLflow tracking server, carrying a CVSS 3.1 base score of 9.3.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N. The Scope:Changed metric is doing real work here: hold every other metric constant and flip Scope to Unchanged and the same flaw scores 8.2. The extra 1.1 points are the scoring system recognising that the vulnerability lets an attacker read cloud credential metadata that unlocks resources well beyond the tracking server, rather than executing commands on the host itself.The affected range is broad: all versions before 3.15.0. That phrasing matters more than usual, because the fix was not backported to any earlier release line. The advisory lists no patched version on the 3.13.x branch or below, so there is nothing you can apply without moving versions. Remediating CVE-2026-64849 means upgrading to 3.15.0 or later, which is a version migration with dependency and compatibility implications, not a routine patch you slot into next Tuesday's window. If your remediation process assumes "apply patch, close ticket", this one will not fit the shape of that process.
The vulnerability was reported by the researcher freeman-bb and independently surfaced through MLflow issue #24179. The project's security advisory GHSA-7gwp-5pfp-969j documents the technical detail.
The KEV listing carries a specific meaning that gets diluted in coverage. CISA does not add vulnerabilities because they score highly or look dangerous. Inclusion requires reliable evidence of active exploitation in the wild. A KEV entry is not a prediction; it is a report that this is already being used against real targets. That changes how you sequence the work, because the window between disclosure and opportunistic mass exploitation has compressed to the point where the first forty-eight hours decide most outcomes for mid-market organisations. To check the published record for this or any other CVE in your stack, use our CVE lookup tool.
The shape of that timeline is the part worth sitting with. The fix was public on 31 July and the advisory two days later. The first publicly reported exploitation came on 17 August, when the CVE identifier was assigned and the flaw became machine-readable to every scanner, feed, and attacker tool that keys off CVE IDs. Whatever was happening quietly before that, the patch had been available for more than a fortnight by the time attackers were visibly looking. That head start did not help the organisations in this post, because you cannot apply a patch to software you do not know you are running.
Knowing a vulnerability is exploited tells you to hurry. Knowing how it works tells you what to check for, and here the mechanism is more instructive than the score.
How the Attack Actually Works
Four moving parts, and the interaction between them is what turns a webhook feature into a credential disclosure path.
The first is the default authentication posture. Running mlflow server with no additional configuration starts a tracking server with no authentication at all. Anyone who can reach the port can read experiments, browse the model registry, and call the API. This is not a hidden trap: MLflow's own network security documentation says plainly that the tracking server should not be exposed to untrusted networks and should sit behind a proxy handling authentication. What happens in practice is that a server deployed inside a VPC boundary in year one acquires an internet-facing load balancer in year two, and nobody revisits the assumption the original deployment was built on.
The second is the model registry webhooks API, also unauthenticated by default. It includes a test endpoint, POST /api/2.0/mlflow/webhooks/{id}/test, which fires the configured webhook synchronously and returns the upstream response body to the caller. That last detail is what makes this severe. Most SSRF is blind: you can make the server send a request but cannot see what came back, so exploitation depends on timing side channels or out-of-band callbacks. Here the response body is handed straight back. It is full-read SSRF, which is the difference between probing your internal network and reading it.
The third part is what makes this an interesting failure rather than a simple oversight. A guard did exist. MLflow PR #20747, shipped in 3.10.0, added _validate_webhook_url, which resolved the hostname in a webhook URL and rejected anything resolving to a non-public IP address. The intent was exactly right: stop webhooks reaching internal services and cloud metadata endpoints.
The fourth part is that the guard had two bypasses, both classic, neither obvious until you have seen them before.
The payoff is cloud credentials. On AWS, a request to the instance metadata service at 169.254.169.254 returns temporary credentials for the IAM role attached to the instance, with equivalents on GCP and Azure. Public reporting on this class of flaw describes attackers using harvested credentials for resource enumeration, deploying cryptominers on available compute, and creating new IAM users so access survives the original vulnerability being closed.
Be precise about what this is. CVE-2026-64849 is credential disclosure via SSRF, not remote code execution. An attacker does not get a shell on the MLflow host. What they get is whatever the stolen identity can do, which is frequently worse.
The Blast Radius Is Bigger Than the Box
The instinct when assessing an exposed internal tool is to ask what is on it. For a compromised tracking server, that is the wrong question. Ask what identity is attached to it, because a stolen instance role gives an attacker whatever that role reaches, from anywhere.
ML infrastructure roles are habitually over-permissioned, for structural rather than careless reasons. Training jobs need broad read access across data stores because feature engineering pulls from wherever the data lives. Artifact logging needs broad write access to model buckets. Nobody knows in advance which datasets the next experiment will touch, so the role is scoped to the class of resource rather than the specific resource. A role written to make experimentation frictionless is, from an attacker's perspective, a general-purpose key to the data platform.
Then the second-order exposures. The artifact store holds trained models, which are intellectual property and sometimes reconstructible training data. The tracking database holds dataset paths that map the internal data estate, and it holds run parameters, which is where credentials get pasted. Logging a connection string or API token as a run parameter is a common shortcut, because it is the fastest way to make an experiment reproducible.
None of this is speculative escalation. The progression from an exposed service to broad lateral movement through a stolen identity is well documented, and we traced exactly that pattern in our analysis of the ShinyHunters Oracle PeopleSoft campaign. The initial foothold is rarely the interesting part of the incident.
See what your external surface exposes, mapped to the controls it touches.
Run a free External Security Check →The Real Problem Is That Nobody Owns the ML Stack
If you fix MLflow this week and change nothing else, you will read a near-identical advisory about a different tool within a year. The vulnerability is the symptom. The condition is an entire tier of infrastructure with no owner.
MLflow frequently arrives as a transitive dependency rather than a procurement decision. It comes bundled inside a platform, gets pulled in by an internal template, or shows up because a data scientist ran the quickstart on a shared instance and it worked. There was no architecture review because nothing was being architected. Organisations sometimes discover an ML service the same way they discover any unattended workload, which is an unexpected line on the cloud bill.
MLflow is a useful example precisely because it is unremarkable. The same applies to Kubeflow pipelines, Ray dashboards, Weights and Biases API keys sitting in notebook environments, Jupyter servers with weak or absent authentication, vector databases holding embedded proprietary content, and model serving endpoints accepting requests from wherever the ingress permits. This is a whole class of infrastructure running with cloud credentials attached that security teams have never formally claimed. The highest-value action available here is not patching one server. It is naming an owner for the category.
Ownership decides whether something gets tested. Which brings us to the document that decides what gets tested.
Why Your Last Pentest Didn't Cover This
Open your most recent penetration test statement of work. Search it for "MLflow". Search it for "machine learning". Search it for "notebook". Search it for the hostname of your ML environment. If all four searches return nothing, you have found the gap without running a single test.
That is not a failure by your testing provider. It is a predictable output of how scope gets written. A scope statement is derived from an asset list, the asset list comes from the CMDB or cloud inventory the security team maintains, and the ML environment is in neither. The provider tests what they are given, thoroughly, and reports accurately. The gap was created before anyone signed anything. We made the same argument about perimeter appliances in our analysis of the Fortinet auth bypasses: patching answers this advisory, scope answers the next one.
Three failure modes recur. Scope derived from a CMDB that predates the ML build, so the scope faithfully describes a stale picture. ML living in a separate cloud account for cost attribution and blast radius reasons while the engagement was scoped to production, so the isolation meant to reduce risk removed the environment from testing. And fingerprinting: an unauthenticated tracking server on a non-standard port behind a load balancer presents to a scanner as unremarkable HTTP, and without someone recognising the response as an ML platform it reads as another low-interest internal web app.
Be honest about what a test of this kind can and cannot establish, because vague claims here are how organisations end up with false assurance.
- Which of your declared hosts answer on which ports
- Whether a responding service fingerprints as an ML platform rather than a generic web application
- Whether that service responds to API requests without authentication
- A dated, evidenced record that a given asset was reachable from the internet on a given day
- Which IAM role is attached and what it can reach (needs a cloud configuration review, not a network test)
- Whether an internal-only instance is exploitable (needs internal access or an assumed-breach engagement in scope)
- What the artifact store actually contains (needs granted authentication)
- Whether MLflow is running in an account you did not declare (testing covers declared scope, not undiscovered assets)
That dated reachability artifact often forces the ownership conversation, because it converts an architectural argument into a documented fact. But the boundary is the point: testing covers declared scope, and it does not discover assets nobody has mentioned. That is exactly why the scope statement, not the test, is the control that matters here.
What Belongs in an AI/ML Scope Statement
The fix is unglamorous. Name the asset classes explicitly in the statement of work, so inclusion does not depend on whether someone remembered to add a hostname to a spreadsheet.
For each class, ask two questions. Is it reachable, and from where? What identity is attached to it? Nearly every serious finding in this area answers one of those two. The first decides whether an attacker can start, the second decides how far they get.
One caveat on that list, and it matters more than it looks. Several of those names are managed third-party services rather than infrastructure you operate. You cannot put a vendor's hosted platform into your own statement of work and have someone test it, because you do not own the target and you cannot grant authorisation you do not hold. For those, the in-scope item is your side of the boundary: the API keys, the service accounts, the network egress rules, and the data you have pushed into them. Testing the vendor's platform itself requires their written authorisation, and any provider willing to skip that step is telling you something about how they will treat your environment.
One clarification, because these get conflated in vendor conversations. This is infrastructure testing, distinct from testing the behaviour of an LLM application itself, which covers prompt injection, output handling, and agent tool abuse; we cover that separately in our guide to LLM and AI application security testing. Both matter, they are different scopes and often different testers, and an engagement covering one does not cover the other.
What To Do This Week
Six steps, in order, most of which need no budget conversation.
/api/2.0/mlflow/webhooks/, particularly the /test endpoint, and any webhook rows in the tracking database pointing at URLs nobody on your team registered. Do not expect to find the outbound request to 169.254.169.254 in your network telemetry: link-local metadata traffic never leaves the instance, so it does not appear in VPC Flow Logs. The reliable signal that credentials were taken is on the identity side. Review CloudTrail for the instance role's session being used from an IP address outside your VPC, and for the follow-on pattern of resource enumeration, new IAM users or access keys, and compute launched in regions you do not operate in. If you run GuardDuty, UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration is the finding that names this directly. KEV means exploited, so this step is not theoretical.MetadataNoToken CloudWatch metric until it sits at zero, then enforce.The Compliance Angle
Be accurate about what the KEV catalog obliges you to do, because overstating it is a fast way to lose the room. The CISA KEV catalog binds US federal civilian executive branch agencies under Binding Operational Directive 26-04, with defined remediation deadlines. It does not create a legal obligation for a private mid-market SaaS or fintech company, though if you sell to the federal government it is worth checking whether your contracts flow the requirement down to you. A security leader who knows this and sees a vendor imply otherwise will discount everything else that vendor says.
What KEV is, for the rest of us, is the highest-quality free signal of confirmed in-the-wild exploitation available anywhere. It is evidence-gated, public, and increasingly referenced outside its formal remit: cyber insurers ask about KEV remediation timelines at renewal, and enterprise security questionnaires have started asking whether KEV entries are tracked. Adopting it as a prioritisation input is defensible on its merits, not because anyone requires it.
The obligation that does apply to many organisations here is PCI DSS Requirement 11.4, which covers internal testing at 11.4.2 and external testing at 11.4.3. Both require penetration testing at least once every twelve months and after any significant infrastructure or application upgrade or change, performed by a qualified tester with organisational independence from the systems under test. Standing up a machine learning platform with cloud credentials attached, network reachability, and access to data stores is a significant change by any reasonable reading. But the twelve-month clock and the significant-change trigger both operate on assets someone has declared. An environment that was never scoped was never tested, so the clock never started and the trigger never fired. The compliance gap and the security gap share a root cause.
That is also the clearest argument for treating exposure management as continuous rather than annual. An asset that appears between two point-in-time assessments is invisible to both, which is the framing behind continuous threat exposure management. Acting on it needs no new category of tool. It needs a scope statement that names the class of asset rather than the instance, and a re-inventory cadence shorter than the rate at which your data team creates infrastructure.
Frequently Asked Questions
What is CVE-2026-64849 and which MLflow versions are affected?
Why did CISA add MLflow to the KEV catalog?
How does the MLflow SSRF vulnerability steal cloud credentials?
Does a penetration test cover MLflow and AI/ML infrastructure?
What should an AI/ML infrastructure penetration test scope include?
Is MLflow exposed to the internet without authentication by default?
mlflow server with default configuration starts a tracking server with no authentication, and MLflow's documentation advises against exposing it to untrusted networks. Exposure usually results from a load balancer or network change made after deployment rather than from the default itself.How do I check whether my MLflow server was already exploited?
/api/2.0/mlflow/webhooks/, especially the /test endpoint, and check the tracking database for webhook URLs nobody registered. The outbound request to 169.254.169.254 will not show up in VPC Flow Logs, because link-local metadata traffic never leaves the instance. The dependable signal is on the identity side: CloudTrail showing the instance role's session used from an IP outside your VPC, followed by resource enumeration, new IAM users or access keys, and compute in unused regions.