SAI Security Advisory

MLflow Vulnerability Report

September 8, 2026

Arbitrary Code Execution via Attacker-Controlled loader_module

‍

CVE Number

CVE-2026-79721

‍

Summary

A malicious MLflow PyFunc model artifact can cause arbitrary code execution when loaded using mlflow.pyfunc.load_model(). The model's MLmodel configuration controls the python_function.loader_module and code settings. During model loading, MLflow adds the configured code directory to sys.path and imports the specified loader module. An attacker who controls the model artifact can therefore cause Python code contained in the artifact to execute with the privileges of the process loading the model.

‍

Products Impacted

This vulnerability is present starting 0.0.1.

‍

CVSS Score: 8.6

CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N

‍

CWE Categorization

CWE-829: Inclusion of Functionality from Untrusted Control Sphere

‍

Details

Mlflow.pyfunc.load_model resolves the supplied model URI to a local model directory, downloading the referenced artifacts when necessary. It then reads the model's MLmodel file and extracts the python_function flavor configuration. 

‍

If the flavor specifies a code path, MLflow adds that path to sys.path before importing the module specified by loader_module.

https://github.com/mlflow/mlflow/blob/8a774946bc19b59cecf3c54a733ebab52aa766f5/mlflow/utils/model_utils.py#L314-L315

def _add_code_to_system_path(code_path):           
      sys.path = [code_path] + sys.path

Both the code and loader_module values originate from the model's MLmodel configuration. By placing the configured code directory at the beginning of sys.path, MLflow allows the subsequent module import to resolve against files contained within the model artifact.

‍

Python executes module-level statements when a module is imported. Consequently, attacker-controlled code executes before MLflow invokes the module's _load_pyfunc() function.

‍

The same execution primitive can also be delivered through remotely hosted MLflow artifacts. When a runs:/ or models:/ URI is supplied, mlflow.pyfunc.load_model() resolves and downloads the referenced model artifacts before processing the MLmodel configuration.

‍

An attacker who can publish or modify a model artifact that is later loaded by another user can therefore deliver both the malicious MLmodel configuration and the corresponding Python module remotely.

# evil_model/MLmodel
artifact_path: model
flavors:
    python_function:
    loader_module: payload
    code: "."
    python_version: "3.9.0"
    env:
        conda: conda.yaml
        virtualenv: python_env.yaml
mlflow_version: 3.1.4
model_uuid: deadbeef-dead-beef-dead-beefdeadbeef
run_id: deadbeef-dead-beef-dead-beefdeadbeef
# evil_model/payload.py
print("PWNED!")

def _load_pyfunc(data_path, model_config=None): ...

Loading the crafted model causes payload.py to be imported. The module-level print() executes during import, causing `PWNED!` to be printed before _load_pyfunc() is invoked.

# poc.py
import mlflow.pyfunc

mlflow.pyfunc.load_model('./evil_model')

Project URL:

https://mlflow.org/

https://github.com/mlflow/mlflow

‍

RESEARCHER: Esteban Tonglet, Security Researcher, HiddenLayer

‍

Related SAI Security Advisory

CVE-2026-87988

September 11, 2026

_READ_ONLY_COMMANDS_POSIX expansion adds 31 commands with no path checking, granting unconditional access to the full host filesystem

Mistral Vibe

Mistral Vibe automatically approves a large set of commands that are not subject to the expected workspace path restrictions, allowing files anywhere on the host to be accessed without user approval.

September 2026
CVE-2026-87987

September 11, 2026

Environment variable prefixes stripped from permission check enable RCE via env injection

Mistral Vibe

Mistral Vibe does not consider environment variable assignments when checking whether a command can run without approval, allowing environment controlled behavior in allowlisted programs such as Git to be abused for arbitrary code execution.

September 2026