Sunday, 25 October 2026
4 agent hacks today▲ 3 vs yesterday (1)

Unauthenticated MLflow servers can be forced to call attacker hosts

A GitHub advisory published on 23 October 2026 says MLflow's scorer endpoints accept unconstrained class names and keyword arguments from anonymous callers.

By The Agentic Times ·

A mechanical hand reaches into an open vault, pulling out unmarked packages while security cameras hang dark and powerless on the walls.
A mechanical hand reaches into an open vault, pulling out unmarked packages while security cameras hang dark and powerless on the walls. · Illustration: The Agentic Times

A security advisory published on 23 October 2026 describes a server-side request forgery flaw in MLflow's tracking server, tracked as CVE-2026-115886 and GHSA-2wqp-q3c6-ccj5. According to the advisory, an unauthenticated attacker can make the server send an outbound HTTP request to any host they choose, with headers and body they control. The flaw affects MLflow installations whose Python environment also has the optional trulens and trulens-providers-litellm packages installed, which the advisory describes as a documented but not bundled setup for generative AI evaluation.

The entry point is POST /ajax-api/3.0/mlflow/scorer/invoke. The advisory states the route is handled by _invoke_scorer_handler in mlflow/server/handlers.py and is guarded only by exception-catching and artifacts-only decorators, with no authentication decorator. The scorer register and GenAI evaluate endpoints are named in the advisory title as similarly affected.

The underlying problem is how MLflow rebuilds a "scorer", a component that grades model output, from JSON supplied by the caller. According to the advisory, Scorer.model_validate_json() reads a module path and a class name out of the attacker's JSON. The module path is checked against an allowlist of four modules: the Ragas, DeepEval, TruLens and Phoenix scorer packages shipped with MLflow. But the advisory says the attribute fetched from that module is never constrained, and neither are the constructor keyword arguments. MLflow then instantiates the named class with those arguments.

For a TruLens scorer, the advisory says the attacker-supplied model URI decides which backend is used. A URI outside MLflow's native provider registry, such as openai:/gpt-4o, falls through to the LiteLLM branch, which forwards the raw keyword arguments into the LiteLLM client. From there, api_base and api_key are passed straight into litellm.completion(). That gives the attacker control of both the destination URL and the Authorization header on the request the server makes.

The advisory includes a working proof of concept. It creates an experiment anonymously, logs a short trace to obtain a trace identifier, then posts a serialised scorer naming the TruLens Coherence class with api_base pointing at a host the attacker runs and api_key set to a marker string. Moments later, the advisory says, the attacker's listener receives a POST /collect/chat/completions request from the server carrying the Authorization: Bearer header the attacker specified. The advisory stresses that the credential in the demonstration is the attacker's own canary value rather than a secret taken from the server. It also notes the vulnerable deserialisation runs synchronously, before any tracking-store lookup or background job submission.

On impact, the advisory says the outbound request reaches anything the server itself can reach, including internal services and cloud metadata endpoints, and that no prior authentication is needed and no operator misconfiguration beyond having the two optional packages installed. The incident record for this issue lists its status as reported.

The advisory does not state a fixed version or list affected version ranges in the material reviewed here. Operators running an MLflow tracking server without authentication, and with the TruLens evaluation extras present, are the group described as exposed.

Sources

  1. github.comhttps://github.com/advisories/GHSA-2wqp-q3c6-ccj5