CVE-2026-116690: Arbitrary File Read via Prompt Model Version Source in MLflow
Vulnerability ID: CVE-2026-116690
CVSS Score: 7.5
Published: 2026-10-27
MLflow versions 3.10.0 through 3.16.1 contain a path traversal vulnerability (CWE-22) in the model version creation handler. Specifically, the prompt-specific execution branch in _create_model_version() fails to validate schemeless relative paths and evaluates the mlflow.prompt.is_prompt tag based on key presence rather than value. An unauthenticated remote attacker on default deployments, or an authenticated low-privilege user on basic-auth deployments, can register a model version with a relative local path as its artifact source and subsequently retrieve arbitrary files from the server filesystem via the /api/2.0/mlflow/model-versions/get-artifact endpoint.
TL;DR
A path traversal flaw in MLflow 3.10.0 to 3.16.1 allows remote attackers to read arbitrary local files from the tracking server by registering a prompt-tagged model version with a schemeless relative source path and downloading artifacts from it.
⚠️ Exploit Status: POC
Technical Details
- CVE ID: CVE-2026-116690
- CWE Classification: CWE-22 (Path Traversal)
- CVSS v3.1 Score: 7.5 HIGH (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)
- Affected Versions: MLflow >= 3.10.0, < 3.17.0
- Patched Version: MLflow 3.17.0
- Attack Vector: Network (HTTP REST API /api/2.0/mlflow/model-versions/create)
- Impact: Arbitrary Local File Read
- Exploit Status: Public Proof-of-Concept (GHSA-m65p-9j7q-6w33)
Affected Systems
- MLflow Tracking Server (mlflow PyPI package)
-
mlflow: >= 3.10.0, < 3.17.0 (Fixed in:
3.17.0)
Code Analysis
Commit: 81730a2
Reject schemeless path sources for prompt model versions in _create_model_version (#25870)
--- a/mlflow/prompt/constants.py
+++ b/mlflow/prompt/constants.py
@@ -5,6 +5,17 @@
# A special tag in ModelVersion to store the prompt text
PROMPT_TEXT_TAG_KEY = "mlflow.prompt.text"
+_CLIENT_PROMPT_SOURCE_PLACEHOLDER = "dummy-source"
+_STORE_PROMPT_SOURCE_PLACEHOLDER = "prompt-template"
+_PROMPT_SOURCE_PLACEHOLDERS = frozenset({
+ _CLIENT_PROMPT_SOURCE_PLACEHOLDER,
+ _STORE_PROMPT_SOURCE_PLACEHOLDER,
+})
+
# Unity Catalog tags cannot contain dots
PROMPT_TYPE_TAG_KEY = "_mlflow_prompt_type"
RESPONSE_FORMAT_TAG_KEY = "_mlflow_prompt_response_format"
--- a/mlflow/server/handlers.py
+++ b/mlflow/server/handlers.py
+def _validate_prompt_source(source: str) -> None:
+ scheme = get_uri_scheme(source)
+ if scheme and scheme != "file":
+ _validate_non_local_source_contains_relative_paths(source)
+ return
+ if source not in _PROMPT_SOURCE_PLACEHOLDERS:
+ raise MlflowException(
+ f"Invalid prompt source: '{source}'. Local source paths are not allowed for prompts.",
+ INVALID_PARAMETER_VALUE,
+ )
+
@@ -3134,21 +3163,7 @@ def _create_model_version():
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(
- f"Invalid prompt source: '{source}'. "
- "Local source paths are not allowed for prompts.",
- INVALID_PARAMETER_VALUE,
- )
- if parsed.scheme:
- _validate_non_local_source_contains_relative_paths(source)
+ _validate_prompt_source(request_message.source)
else:
if request_message.model_id:
_validate_source_model(request_message.source, request_message.model_id)
@@ -3213,7 +3228,10 @@ def _create_model_version():
def _is_prompt_request(request_message):
- return any(tag.key == IS_PROMPT_TAG_KEY for tag in request_message.tags)
+ tags = {tag.key: tag.value for tag in request_message.tags}
+ return tags.get(IS_PROMPT_TAG_KEY, "false").lower() == "true"
Exploit Details
- GitHub: Official GitHub Security Advisory detailing the HTTP reproduction steps and vulnerable code path.
Mitigation Strategies
- Upgrade MLflow to version 3.17.0 or later to enforce strict placeholder validation on prompt model version sources.
- Deploy an authenticating reverse proxy or API gateway in front of MLflow tracking servers to prevent unauthenticated network access.
- Implement WAF or reverse proxy inspection rules to block POST requests to
/api/2.0/mlflow/model-versions/createcontaining relative path traversal sequences (..) in thesourcefield. - Run the MLflow server process under a dedicated, least-privilege operating system user account with restricted filesystem read permissions.
Remediation Steps:
- Update the MLflow Python package via pip:
pip install --upgrade "mlflow>=3.17.0". - Restart all running
mlflow serverinstances and worker processes to load the patched handler code. - Audit the MLflow backend registry database (
model_versionstable) for existing entries with anomalous or relativesourcepaths and delete malicious model versions. - Rotate any sensitive credentials, API keys, or authentication database secrets stored on the server filesystem if unauthorized access is suspected.
References
- GitHub Security Advisory GHSA-m65p-9j7q-6w33
- MLflow Pull Request #25870: Reject schemeless path sources for prompt model versions
- Patch Commit 81730a2ce4ebcd60d5d18f68c96f422ad7e30772
- MLflow Release v3.17.0
- CVE.org Record: CVE-2026-116690
- NVD Detail: CVE-2026-116690
- CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
- MITRE ATT&CK T1083: File and Directory Discovery
- MITRE ATT&CK T1005: Data from Local System
Read the full report for CVE-2026-116690 on our website for more details including interactive diagrams and full exploit analysis.
Top comments (0)