On this page (7 sections)

A critical vulnerability in MLflow, tracked as CVE-2026-116692, allows an unauthenticated HTTP caller to execute arbitrary Python code on an MLflow server that runs without authentication. The flaw sits in the handling of third-party scorers, a feature added in MLflow 3.12.0, and is fixed in 3.17.0, according to the GitHub advisory.

If you run an MLflow tracking server that is reachable from a network you do not fully control, and you have not enabled authentication, treat this as an upgrade-now item. Only one source covers this story so far, so details below come from that advisory.

What happened

MLflow 3.12.0 added support for registering and deserializing third-party scorers in the open-source server. When the server deserializes a scorer, it takes a Python module path from the request, checks it against a short list of allowed package prefixes, imports that module, looks up a class name also supplied by the request, and creates an instance of it right away.

The advisory was published on Oct 9, 2026 and updated on Oct 27, 2026. Its author reproduced the issue on the default no-auth server using the official MLflow v3.14.0 image, where it produced unauthenticated arbitrary Python code execution with the privileges of the MLflow server process. In that image the process runs as uid 0 (root) and can write to its installed Python package tree.

Why the allow-list did not help

The allow-list is a set of four module prefixes under the mlflow.genai.scorers package (ragas, deepeval, trulens and phoenix). The deserializer accepts either an exact match or any module whose path starts with one of those names followed by a dot. That is a string check on a name, not a check on where the code actually came from.

The advisory explains that MLflow's own features can put attacker-supplied files under one of those prefixes. An experiment can be created with a file-based artifact location, and runs store artifacts in a predictable subdirectory of that location. If the server process can write inside an allowed package directory, a file uploaded as a run artifact becomes an importable module whose name still satisfies the prefix check.

Put simply, the server trusted a name, and the attacker was able to create something with that name. The advisory's own summary of the chain is that an HTTP caller combines the experiment, run and artifact-upload endpoints with the scorer invocation endpoint, which then imports and constructs the uploaded class.

Execution happens before validation

According to the advisory, the invocation handler deserializes the scorer first and only afterward resolves trace IDs. The attacker-controlled constructor therefore runs before the server validates anything else. The advisory also notes that no background job execution or special job-execution server option is needed to reach this code path. An error returned afterward does not mean the code did not run.

Who is affected

  • Servers running mlflow from 3.12.0rc0 up to but not including 3.17.0.
  • Servers with authentication disabled, which is the default configuration reproduced in the advisory. No credentials are needed in that case.
  • Deployments where the server process can write to its own Python package directory, such as the official image that runs as root.
  • Servers exposed beyond a trusted network, for example published on the internet or reachable from many internal hosts.

The advisory states that when MLflow basic authentication is enabled, authentication still applies. The research text is cut off before it finishes describing how that affects the scorer invocation endpoint, so do not assume enabling authentication alone removes the risk. Upgrading is the fix.

How to check your exposure

First find out which version you run. This read-only command, run in the Python environment that serves MLflow (or inside the container), prints the installed version. Anything from 3.12.0rc0 to below 3.17.0 is affected.

Show the installed MLflow version (run in the server's Python environment)Shell
pip show mlflow

If you use the official container image, you can run the same query inside the running container. Replace the placeholder with your container name.

Show the MLflow version inside a running container (replace the name)Shell
docker exec <container_name> pip show mlflow

Then check who can reach the server and whether authentication is on. A server answering on a public address or a wide internal range without a login is the highest-risk case. You can also test from outside your network to confirm what is actually exposed, and review your security headers and TLS setup with the free security headers checker and SSL checker.

What to do now

Caution. Upgrading restarts your MLflow server and may interrupt running experiments or tracking calls. Schedule it, back up your backend store and artifact storage first, and test in staging if you can.

This command upgrades the package to the fixed release. Run it in the same environment as the server, then restart the service and re-run the version check to confirm 3.17.0 or later.

Upgrade MLflow to the fixed version (run in the server's Python environment)Shell
pip install --upgrade "mlflow>=3.17.0"
Restricting write access to the package tree is a sensible hardening step based on how the advisory describes the attack, but it is not the vendor's stated fix. The fix is the upgrade to 3.17.0.

Detection and monitoring

The advisory's chain leaves traces. The attack creates experiments whose artifact location points into an MLflow package directory and uploads a Python file as a run artifact. The resulting files sit under a path that looks like the scorers package, then a run ID, then an artifacts folder. This read-only search, run on the server, lists Python files in that unusual location. Any results deserve review, since legitimate installs do not normally contain run-ID directories there.

List Python files placed under the scorers package tree (adjust the path to your install)Shell
find /usr/local/lib/python3.10/site-packages/mlflow/genai/scorers -path '*artifacts*' -name '*.py'

Also review web or proxy logs for requests to the scorer invocation endpoint (/ajax-api/3.0/mlflow/scorer/invoke) from unfamiliar addresses, and for experiment creation calls that set an artifact location outside your normal storage. The research does not report in-the-wild exploitation, so absence of hits is reassuring but not proof.

If you find signs of compromise, follow a standard incident process, and see our guide on what to do when a website is hacked. For ongoing visibility of what your public endpoints expose, Webforta Monitor can watch for certificate, DNS and header changes, though it does not detect this specific flaw.

Takeaways

This is a familiar pattern: a name-based allow-list guarding dynamic imports, combined with a feature that lets callers write files where imports resolve. Internal tooling such as ML tracking servers is often deployed without authentication on the assumption that the network is trusted. That assumption fails when one reachable host is enough. Upgrade, restrict access and run services with least privilege. For broader guidance, see our small business website security guide.

Sources

This article is based on the public sources below. Details can change as vendors and researchers publish more; check the original advisories before acting on version numbers.

  1. CVE-2026-116692 - GitHub Advisory Databasegithub.com
  • MLflow
  • RCE
  • Python
  • deserialization
  • MLOps