Introduction
A recently patched flaw in MLflow lets a remote, unauthenticated attacker point the tracking server at any host it chooses. That turns the platform's artifact handling into a server-side request forgery primitive against internal services and cloud metadata endpoints. The open-source MLflow tracking server ships without authentication by default, so on those deployments anyone with network access to the service could read internal resources in the server's own context.
MLflow is an open-source machine learning lifecycle platform created by Databricks and now hosted under the Linux Foundation. It covers experiment tracking, reproducible execution, and model sharing, and the project reports over 30 million monthly downloads and use by thousands of organizations and research teams. That scale, plus its common deployment on internal networks alongside cloud credentials, makes its request handling an attractive SSRF target.
Technical Information
The root cause of CVE-2026-118628 is inadequate validation of a URI before the server accesses the resource it names. ZDI identifies both the vendor and product as MLflow, credits Muhammad Fadilullah Dzaki, and tracks the issue internally as ZDI-CAN-28192. NVD classifies the weakness as CWE-918, Server-Side Request Forgery.
The tracking server accepted any URI that a client supplied in an experiment's artifact_location, a model version's source, or a workspace's storage settings. When the server later handled get-artifact, model version downloads, UI uploads, logged model artifacts, or trace payloads, it passed that stored URI to the artifact repository registry. Several repository types then open a connection to whatever host and port the URI names, including ftp, sftp, hdfs, viewfs, r2, b2, abfss, plus http, https, and mlflow-artifacts://host when the URI is not proxied. Nothing revalidated that destination, so a client could make the server connect to any host it selected.
The published ZDI assessment uses the vector CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N for a score of 8.2. That means a network attack origin, low complexity, no privileges, no user interaction, unchanged scope, high confidentiality impact, and low integrity impact. Because the OSS tracking server runs without authentication by default, the assessment states that authentication and user interaction are not required.
At the advisory level, the attacker invokes vulnerable artifact functionality with an inadequately validated URI, and the server accesses the resource in its own context. The advisory narrative documents disclosure of sensitive information. The vector includes a low integrity impact, but the reviewed CVE description does not explain a separate technique for modifying data, and it does not establish remote code execution or denial of service.
The trust boundary at stake is that client-supplied storage metadata must not automatically decide where the server connects. The patch enforces that boundary directly instead of relying only on restricting who can send an incoming request.
Proof of Concept
A public, request-level proof of concept exists for this flaw. The reproduction steps are in MLflow security issue #24767, titled "[Security] SSRF via artifact_location in create-experiment." The issue notes that the OSS tracking server runs without authentication by default and that artifact_location accepts any URL scheme, with no scheme validation and no IP blocklist. The server accepts internal and metadata targets such as 127.0.0.1 and 169.254.169.254, stores the host-addressed location, and later dereferences it when artifacts are fetched. That dereference is the SSRF.
The PoC is a two-step HTTP request sequence, not a standalone exploit script.
First, create an experiment whose artifact location points at an internal or metadata target:
POST /api/2.0/mlflow/experiments/create {"name": "test", "artifact_location": "http://169.254.169.254/latest/meta-data/"}
Second, request the experiment's artifacts to trigger the outbound request:
GET /api/2.0/mlflow/experiments/{id}/artifacts
The server then sends a request to the attacker-specified host, in this case the cloud metadata endpoint. From there, an attacker can reach internal services, read cloud metadata for AWS, GCP, or Azure credentials, and scan internal ports. The GitHub issue does not cite the CVE ID, and it gives reproduction guidance rather than a packaged exploit. Its described flaw, affected endpoint, and linked vendor fix in PR #25891 match the CVE-2026-118628 and ZDI-26-844 advisory exactly.
Patch Information
According to the ZDI advisory, MLflow fixed this SSRF in MLflow 3.17.0. The advisory links to pull request #25891, titled "Refuse to connect to client-named artifact hosts outside the default artifact root (GHSA-mr9f-g8qf-4w4j)." The PR was merged on 2026-09-19 as merge commit 53e21239 and touched 9 files, with +800 and -12 lines.
The fix adds a new module, mlflow/store/artifact/host_policy.py. When a URI uses one of the guarded schemes, the server now connects only if the URI's scheme family, hostname, and effective port match the server's own --default-artifact-root or --artifacts-destination. Normal experiments inherit locations on those operator-configured hosts, so a standard deployment should behave the same as before.
The core decision function, as shipped:
def rejected_host_addressed_scheme(uri: str) -> str | None: targets = host_addressed_uri_targets(uri) if targets is None: return None scheme = get_uri_scheme(uri) if scheme in {s.lower() for s in MLFLOW_ALLOWED_HOST_ADDRESSED_ARTIFACT_SCHEMES.get()}: return None if targets & _trusted_targets(): return None return scheme
The policy also closes common ways around an SSRF filter:
- Port normalization: if a URI has no port, the policy fills in the scheme's well-known port before comparing, so
http://hostandhttp://host:80count as the same target buthttps://hostdoes not match anhttp://hostroot. A portlessmlflow-artifacts://hostcounts as both 80 and 443. HDFS gets no default port because its port comes from the Hadoop client configuration, so a portless HDFS URI only matches a root that is also portless, and uncertain cases are refused. - Scheme families:
viewfsis treated ashdfs, andhttpsandmlflow-artifactsare treated ashttp, so picking a sibling scheme does not bypass the check. - Hostless URIs: a URI like
ftp:///pathstill connects to localhost, so it is not exempt. The hostname is recorded as an empty string instead of being skipped. - Parser differentials: if the authority contains a backslash or whitespace, the URI is marked malformed and never trusted, because HTTP clients end the authority at a backslash but
urlparsedoes not. A port that will not parse is also treated as malformed.
The check runs at three layers. The registry level is the main choke point, applied in ArtifactRepositoryRegistry.get_artifact_repository:
+from mlflow.store.artifact.host_policy import enforce_server_artifact_uri_host_policy ... + # Applied here rather than only in the server handlers so that repositories resolved + # indirectly (`runs:/`, `models:/`) and those built by the tracking client inside server + # jobs follow the same policy. + enforce_server_artifact_uri_host_policy(artifact_uri) scheme = get_uri_scheme(artifact_uri)
That enforcement acts only when the _MLFLOW_SERVER_ARTIFACT_ROOT environment variable is set. The mlflow server command exports it to its workers, and the huey consumer and job subprocesses inherit it, so ordinary client processes are unaffected. When a URI is submitted, _validate_artifact_uri_scheme runs in the shared storage location validator, which covers experiment artifact_location, workspace default_artifact_root, and the workspace trace archival location, and it also runs in _create_model_version. It rejects a bad location immediately with a 400 error that names the field. When a stored URI is used, _get_artifact_repository_for_uri wraps repository construction, so handlers stay protected even without the mlflow server CLI environment.
The same PR also removes a @catch_mlflow_exception decorator from the internal helper _get_artifact_repo. That decorator used to turn an exception into an HTTP Response object, which the calling handler then treated as an artifact repository. With it gone, errors, including the new policy rejections, reach the decorated handler and are reported correctly.
Operator notes from the PR:
- Operators who legitimately store artifacts on a non-default host can set the opt-in
MLFLOW_ALLOWED_HOST_ADDRESSED_ARTIFACT_SCHEMESenvironment variable, a comma-separated, case-insensitive list that is empty by default. Setting it reopens SSRF for that scheme across every host. - Workspace-specific default roots on a different host are not trusted automatically.
- If the WSGI app is run directly instead of through
mlflow server, the same server environment variables must be exported manually, or the registry-level check stays off.
Tests were added in tests/store/artifact/test_host_policy.py, the registry tests, test_handlers.py, and test_workspace_endpoints.py. They cover rejection of every guarded scheme, near-miss host, port, and scheme cases, refusal behind runs:/ URIs, and trusted or proxied cases that should still work.
For SQL-backed servers, the 3.17.0 release notes require a coordinated schema upgrade with stopped writers and a restorable backup. Administrators should run mlflow db upgrade <database-url> before restarting all replicas on the new version, because mixed-version rolling upgrades are not supported.
Affected Systems and Versions
NVD identifies MLflow 3.8.0 as affected, but the reviewed ZDI advisory does not specify the complete range of affected versions. The sources confirm one affected version and one fixed release, and nothing more. Do not invent a lower or upper bound, and do not treat the single NVD version as a complete inventory of vulnerable releases.
- Confirmed affected version: MLflow 3.8.0 (per NVD).
- Confirmed fixed release: MLflow 3.17.0, published October 7, 2026 (per ZDI and the release page).
- Default exposure factor: the OSS tracking server runs without authentication by default, and the vulnerability requires none.
- Unconfirmed: the complete affected version range and whether the Databricks-managed MLflow offering is affected.
Disclosure timeline: vendor notification on February 5, 2026; fixed release MLflow 3.17.0 published October 7, 2026; coordinated ZDI advisory released October 29, 2026; NVD publication November 4, 2026. The fixed release predates both the advisory and the NVD record, so the NVD publication date is not the date remediation first became available.
Vendor Security History
MLflow has several security advisories affecting different trust boundaries. A fix for one request path does not necessarily repair another.
CVE-2026-64849 is a separate MLflow webhook SSRF. Its original URL validation could be bypassed through redirects, because redirect destinations were not revalidated and the originally resolved address was not pinned. That advisory documents full response disclosure from internal destinations and a confirmed test against MLflow 3.13.0. It affects versions 3.10.0 through 3.14.x and is fixed in 3.15.0, where the repair adds connection-time peer IP validation that also protects redirect connections. The webhook case is about where an HTTP connection finally lands after validation and redirection. The artifact case here is about which client-supplied artifact destinations the server accepts.
CVE-2026-2652 documents authentication coverage gaps between Flask and FastAPI, where certain job and trace routes remained unprotected even when basic authentication was enabled in the affected configuration. Turning authentication on did not, by itself, mean every route was covered.
Taken together, these advisories support evaluating specific versions, routes, and fixes rather than assigning MLflow a single overall security rating.
References
- NVD: CVE-2026-118628
- ZDI-26-844 advisory
- MLflow security issue #24767 (SSRF via artifact_location)
- MLflow pull request #25891 (host policy fix)
- GitHub API: pull request #25891 metadata
- GitHub API: merge commit 53e21239
- host_policy.py source
- MLflow 3.17.0 release notes
- OffSeq Threat Radar record for ZDI-26-844
- MLflow webhook SSRF advisory GHSA-7gwp-5pfp-969j (CVE-2026-64849)
- MLflow FastAPI auth advisory GHSA-75cm-x2w3-8mgf (CVE-2026-2652)
- MLflow issue #23500 (mass SSRF claims)
- MLflow security policy
- MLflow Tracking Server documentation
- MLflow basic HTTP authentication documentation
- MLflow project website
- The MLflow Project Joins Linux Foundation
- MLflow on Databricks



