CVE-2026-96804 Overview
CVE-2026-96804 is an insecure deserialization vulnerability in the MLflow statsmodel model flavor. The flaw affects versions 2.1.0 through 3.14.0. The _load_model() function omits the MLFLOW_ALLOW_PICKLE_DESERIALIZATION=False security control entirely, leaving pickle-based model loading unrestricted. A remote attacker with low privileges can execute arbitrary code by supplying a crafted MLmodel artifact. The vulnerability maps to CWE-502: Deserialization of Untrusted Data.
Critical Impact
Loading a malicious statsmodel artifact triggers arbitrary code execution in the MLflow process, compromising confidentiality, integrity, and availability of the model registry host.
Affected Products
- MLflow statsmodel flavor, version 2.1.0
- MLflow statsmodel flavor, versions 2.1.0 through 3.14.0
- MLflow deployments that load third-party or user-supplied statsmodel artifacts
Discovery Timeline
- 2026-09-23 - CVE-2026-96804 published to NVD
- 2026-09-24 - Last updated in NVD database
Technical Details for CVE-2026-96804
Vulnerability Analysis
MLflow provides model flavors that wrap serialization and loading logic for different machine-learning frameworks. The statsmodel flavor persists models using Python pickle by default. MLflow introduced the MLFLOW_ALLOW_PICKLE_DESERIALIZATION environment flag so operators can block untrusted pickle loading across flavors.
In versions 2.1.0 to 3.14.0, the statsmodel flavor's _load_model() function does not consult this flag. The gate is missing from the code path, so pickle deserialization runs unconditionally. Setting MLFLOW_ALLOW_PICKLE_DESERIALIZATION=False provides no protection for statsmodel artifacts.
An attacker who can register, upload, or substitute an MLmodel artifact can embed a malicious pickle payload. When a downstream user or automated pipeline calls mlflow.statsmodels.load_model(), Python executes attacker-controlled code inside the loading process. Refer to the CERT Vulnerability Advisory #369093 for further technical details.
Root Cause
The root cause is a missing security control in the _load_model() implementation of the statsmodel flavor. The function invokes pickle deserialization without checking the MLFLOW_ALLOW_PICKLE_DESERIALIZATION environment variable that other flavors honor. This is a classic instance of [CWE-502], where untrusted serialized data drives object reconstruction and callable execution.
Attack Vector
The attack vector is network-based and requires low privileges. An attacker needs the ability to place or reference a crafted MLmodel artifact reachable by the MLflow server or client. Typical scenarios include:
- A contributor with model registry write access uploading a poisoned artifact.
- A shared artifact store (for example, S3, Azure Blob, or a shared filesystem) where an attacker can write or replace files.
- A downstream consumer loading a model pulled from an untrusted registry or URI.
Exploitation succeeds the moment a victim process calls the statsmodel load routine on the malicious artifact. The malicious payload runs with the privileges of the MLflow user, providing a foothold for lateral movement into training pipelines, secrets, and cloud credentials.
Detection Methods for CVE-2026-96804
Indicators of Compromise
- Unexpected MLmodel or model.pkl files in artifact stores that were not produced by known training jobs.
- Child processes spawned by MLflow workers that execute shells, curl, wget, or Python one-liners immediately after a load_model call.
- Outbound network connections from MLflow tracking or serving hosts to unfamiliar domains shortly after model loads.
- New credentials, SSH keys, or cron entries created on MLflow hosts following artifact ingestion.
Detection Strategies
- Instrument MLflow servers to log every call to mlflow.statsmodels.load_model() with the source URI and calling user.
- Alert on python or mlflow processes spawning interactive shells, package installers, or reverse-shell tooling.
- Baseline network egress from MLflow infrastructure and flag deviations, especially connections to raw IPs or paste sites.
Monitoring Recommendations
- Enable audit logging on artifact stores to record who wrote or modified MLmodel files.
- Forward MLflow application logs and host process telemetry to a centralized analytics platform for correlation.
- Monitor registry activity for artifact overwrites, version reassignments, or tag changes performed by low-privilege accounts.
How to Mitigate CVE-2026-96804
Immediate Actions Required
- Inventory all MLflow deployments and confirm whether the statsmodel flavor is in use.
- Restrict write access to model registries and artifact stores to trusted service accounts only.
- Quarantine and re-review any statsmodel artifacts sourced from external contributors or public registries.
- Isolate MLflow tracking and serving hosts on network segments with strict egress controls.
Patch Information
Upgrade MLflow to a version later than 3.14.0 that restores enforcement of MLFLOW_ALLOW_PICKLE_DESERIALIZATION in the statsmodel _load_model() path. Consult the CERT Vulnerability Advisory #369093 and the MLflow project release notes for the fixed version and upgrade guidance.
Workarounds
- Avoid loading statsmodel artifacts from untrusted sources until the upgrade is applied.
- Run MLflow workers under least-privilege service accounts with no access to production secrets.
- Wrap load_model calls in a sandboxed process or container with restricted network and filesystem access.
- Validate artifact provenance using signed manifests or hash allowlists before loading.
# Configuration example: least-privilege service isolation for MLflow workers
# Restrict outbound network and filesystem scope for the mlflow user
sudo useradd --system --home /var/lib/mlflow --shell /usr/sbin/nologin mlflow
sudo chown -R mlflow:mlflow /var/lib/mlflow
# Deny egress except to the artifact store and tracking DB
sudo iptables -A OUTPUT -m owner --uid-owner mlflow -d <artifact-store-cidr> -j ACCEPT
sudo iptables -A OUTPUT -m owner --uid-owner mlflow -d <tracking-db-cidr> -j ACCEPT
sudo iptables -A OUTPUT -m owner --uid-owner mlflow -j REJECT
# Enforce the pickle deserialization flag at the process level (defense in depth)
export MLFLOW_ALLOW_PICKLE_DESERIALIZATION=False
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.