---
title: "MLflow CVE-2026-116692: Unauthenticated RCE via Scorers"
url: https://erroneo.us/security/briefs/mlflow-scorer-deserialization-cve-2026-116692-rce/
description: "MLflow 3.12.0-3.16.x servers without auth let anyone run code by uploading a fake scorer module. Fixed in 3.17.0; PoC public. Patch exposed servers this week."
type: security-brief
section: "Security Digest"
date_published: 2026-10-27
date_modified: 2026-10-28
publisher: erroneo.us
author: erroneo.us Security Desk
verdict: "Patch this week"
cves: ["CVE-2026-116692"]
severity: "critical"
exploited: "public-poc"
kev: false
license: All rights reserved. Quote with attribution and a link.
---

# MLflow CVE-2026-116692: Unauthenticated RCE via Scorers

**Verdict: Patch this week.** No exploitation is reported, but the advisory spells out a five-step chain to code execution on default no-auth MLflow servers, so upgrade to 3.17.0 or later this week and today for any server reachable beyond your own team.

- **Who must act:** Teams running an MLflow tracking server from 3.12.0rc0 up to 3.17.0, especially the official Docker image or any install where the server process can write to its own Python packages, and above all servers reachable without authentication.
- **Who can relax:** Users of MLflow below 3.12.0 or at 3.17.0 and later, local-only servers on the default 127.0.0.1 binding, people who only use the MLflow client library, and servers whose package directories are read-only to the server process.
- **Affected:** MLflow 3.12.0rc0 through 3.16.x (pip: mlflow >= 3.12.0rc0, < 3.17.0), including ghcr.io/mlflow/mlflow images in that range
- **Fixed:** MLflow 3.17.0 (released October 7, 2026; 3.17.2 is current)
- **CVEs:** CVE-2026-116692
- **CISA KEV:** no · **Exploitation:** Public proof-of-concept

## What to do

1. Check every tracking server: pip show mlflow, or docker exec <container> pip show mlflow; anything from 3.12.0rc0 up to 3.17.0 is affected.
2. Upgrade to 3.17.0 or later (pip install -U "mlflow>=3.17.0" or pull a 3.17.x image) and restart the server.
3. Until upgraded, take the server off untrusted networks and put it behind authentication (MLflow basic auth, a VPN or an identity-aware proxy).
4. Make the server's Python package tree read-only: run the container as a non-root user with a read-only root filesystem; the advisory says this blocks the chain.
5. If an affected server was reachable without authentication, run the checks below and rotate every credential the server process could read (artifact store keys, database URIs, cloud roles, LLM API keys).

### How to check if you are affected

- Search access logs for POST /ajax-api/3.0/mlflow/scorer/invoke, especially from clients you do not recognize.
- List experiments with search_experiments(view_type=ViewType.ALL) and flag any whose artifact_location points into site-packages or the mlflow/genai/scorers package.
- Look for run-artifact directories or unexpected .py files inside the installed mlflow package tree, for example under mlflow/genai/scorers/.
- Check the server host or container for processes, outbound connections or files the MLflow service does not normally create.

## What happened

On October 27, MLflow published [GHSA-26p8-2jq9-3vq9](https://github.com/mlflow/mlflow/security/advisories/GHSA-26p8-2jq9-3vq9), assigned [CVE-2026-116692](https://www.cve.org/CVERecord?id=CVE-2026-116692) and scored CVSS 3.1 9.8 by GitHub, the CNA. MLflow 3.12.0 added support for registering and deserializing "third-party scorers", the evaluation plugins built on TruLens, Ragas, DeepEval and Phoenix. When the server deserializes a scorer, it takes a Python module path and class name from the request. It checks only that the module path begins with one of the allowed package names, then imports the module and instantiates the class.

A prefix is not a trust boundary. According to the advisory, an HTTP caller can chain built-in MLflow endpoints:

1. Create an experiment whose local artifact location points inside a writable allowed scorer package, such as `mlflow/genai/scorers/trulens`.
2. Create a run, which makes MLflow allocate `<artifact_location>/<run_id>/artifacts/`.
3. Upload a Python file as a run artifact.
4. Submit a serialized scorer whose module path resolves to that file.
5. Call `/ajax-api/3.0/mlflow/scorer/invoke`, which imports the file and runs its code inside the server process.

The reporter reproduced this on the official `ghcr.io/mlflow/mlflow:v3.14.0` image with default settings. That image runs as uid 0 and lets the process write to its own package tree, so the result is unauthenticated arbitrary Python execution as root inside the container. The advisory includes a harmless proof of concept.

The fix, [PR #25906](https://github.com/mlflow/mlflow/pull/25906), replaces the prefix match with an exact allow-list of 14 modules. It also requires the resolved class to be a `Scorer` subclass and rejects dotted metric names. It shipped in [MLflow 3.17.0](https://github.com/mlflow/mlflow/releases/tag/v3.17.0), which [PyPI](https://pypi.org/project/mlflow/#history) shows was released on October 7, three weeks before the advisory. The same release fixed two related scorer issues, including missing authorization on `scorer/invoke` (GHSA-6c27-cp6h-c66m).

## Who is affected

MLflow tracking servers from 3.12.0rc0 up to, but not including, 3.17.0. All three of these conditions must hold:

- **Reachable:** the attacker can send HTTP requests to the server. `mlflow server` binds to 127.0.0.1 by default, but shared team servers, Kubernetes deployments and Docker setups usually listen more widely.
- **Allowed by authentication:** with authentication off, the default, no credentials are needed. With basic auth on, the attacker needs an account that can create experiments and runs and upload artifacts, which on many shared servers is every user.
- **Writable packages:** the server process can write beneath an allowed scorer package. The official image allows this. A read-only container filesystem, or a package directory owned by another user, blocks the chain.

Using MLflow only as a client library, logging to a hosted or patched server, is not affected.

## Do you really need to act?

If you run a shared MLflow server in the affected range, yes, this week. No exploitation has been reported and CISA has not listed the CVE. However, the advisory documents the chain step by step, every step uses a normal MLflow API, and the default configuration of the official image meets every precondition. MLflow servers also tend to hold valuable secrets: artifact store keys, database URIs, cloud roles and, increasingly, LLM provider API keys. Attackers have exploited MLflow before. An unauthenticated SSRF, CVE-2026-64849, was added to CISA's KEV catalog on August 19, 2026.

A server that only binds to localhost on a developer laptop is a low priority. Exposed servers without authentication are not, and they should be patched today.

## What to do now

1. Check versions: `pip show mlflow` on the server host, or `docker exec <container> pip show mlflow`.
2. Upgrade: `pip install -U "mlflow>=3.17.0"`, or move to a 3.17.x image, then restart. 3.17.2 is the current release.
3. Until upgraded, remove the server from untrusted networks and require authentication in front of it, using MLflow basic auth, a VPN or an identity-aware proxy.
4. Harden the deployment whatever version you run. Use a non-root user and a read-only root filesystem (`docker run --read-only --user 1000:1000 ...` with a separate writable volume for artifacts), so that uploaded files can never land inside importable code.
5. If an affected server was reachable without authentication, run the checks below and rotate the credentials the server process could read.

## How to tell if you were hit

- Search access logs for `POST /ajax-api/3.0/mlflow/scorer/invoke` from unfamiliar clients, particularly right after experiment creation, run creation and artifact uploads from the same source.
- List experiment artifact locations, including deleted experiments: `python -c "import mlflow; from mlflow.entities import ViewType; [print(e.experiment_id, e.artifact_location) for e in mlflow.search_experiments(view_type=ViewType.ALL)]"` with `MLFLOW_TRACKING_URI` set. Any location inside `site-packages` is a red flag.
- Look for uploaded files inside the installed package: `find "$(python -c 'import mlflow, os; print(os.path.dirname(mlflow.__file__))')/genai/scorers" -name artifacts -type d`.
- Review the container or host for unexpected processes, outbound connections and files.

## Timeline

- May 5, 2026 — MLflow 3.12.0 adds third-party scorer deserialization.
- Sep 16, 2026 — The fix (PR #25906) is merged.
- Oct 7, 2026 — MLflow 3.17.0 is released with the fix.
- Oct 27, 2026 — The advisory and CVE-2026-116692 are published.

## Sources

1. [MLflow — GHSA-26p8-2jq9-3vq9: Remote code execution via third-party scorer deserialization](https://github.com/mlflow/mlflow/security/advisories/GHSA-26p8-2jq9-3vq9)
2. [CVE record — CVE-2026-116692](https://www.cve.org/CVERecord?id=CVE-2026-116692)
3. [MLflow — PR #25906: Match third-party scorer modules exactly instead of by package prefix](https://github.com/mlflow/mlflow/pull/25906)
4. [MLflow — Release v3.17.0](https://github.com/mlflow/mlflow/releases/tag/v3.17.0)
5. [PyPI — mlflow release history](https://pypi.org/project/mlflow/#history)
6. [GitHub Advisory Database — GHSA-26p8-2jq9-3vq9](https://github.com/advisories/GHSA-26p8-2jq9-3vq9)
7. [Mend.io — WS-2026-0135: MLflow third-party scorer deserialization RCE](https://www.mend.io/vulnerability-database/WS-2026-0135/)

---
Source page: https://erroneo.us/security/briefs/mlflow-scorer-deserialization-cve-2026-116692-rce/ · Security Digest: https://erroneo.us/security/ · More: https://erroneo.us/llms.txt
