MLflow / MLServer Insecure Pickle Deserialization RCE — CVE-2026-0596
by SparshBiswas-AI · 2026-07-05
- Severity
- Critical
- CVE
- CVE-2026-0596 (GHSA-rvhj-8chj-8v3c)
- Category
- web
- Affected product
- MLflow model serving stack (mlflow==2.11.1, mlserver==1.3.5, mlserver-mlflow==1.3.5)
- Affected versions
- mlflow 2.11.1 and mlserver 1.3.5 deployed with enable_mlserver=True (legacy pickle-based model artifacts)
- Disclosed
- 2026-07-05
- Patch status
- unpatched
Archive entry
intelseclab/poc-archiveMetadata
| Field | Value |
|---|---|
| Date Added | 2026-07-05 |
| Last Updated | 2026-05 |
| Author / Researcher | SparshBiswas-AI |
| CVE / Advisory | CVE-2026-0596 (GHSA-rvhj-8chj-8v3c) |
| Category | web |
| Severity | Critical |
| CVSS Score | 9.6 Critical (huntr.dev CNA) / 7.8 High (NVD) |
| Status | PoC |
| Tags | mlflow, mlserver, pickle, insecure-deserialization, rce, machine-learning, cwe-502, docker |
| Related | N/A |
Affected Target
| Field | Value |
|---|---|
| Software / System | MLflow model serving stack (mlflow==2.11.1, mlserver==1.3.5, mlserver-mlflow==1.3.5) |
| Versions Affected | mlflow 2.11.1 and mlserver 1.3.5 deployed with enable_mlserver=True (legacy pickle-based model artifacts) |
| Language / Platform | Python 3.10, containerized via Docker |
| Authentication Required | No |
| Network Access Required | Yes (model-serving REST endpoint) / Local-equivalent when loading a poisoned artifact |
Summary
MLflow can serve models through Seldon’s MLServer runtime, which loads model artifacts using Python’s native pickle format. While the REST API’s string parameters are handled safely and are not vulnerable to classic OS command injection, the underlying artifact-loading path calls pickle.load() on model files without any restriction on the object graph being reconstructed. An attacker who can supply or substitute a model artifact can embed a __reduce__ hook that invokes os.system (or any other callable) the instant the object is deserialized, achieving arbitrary code execution inside the serving container before any inference logic runs. The included lab first proves that shell metacharacter injection into API parameters is not exploitable, then demonstrates the real vulnerability using a crafted pickle payload.
Vulnerability Details
Root Cause
pickle.load() executes attacker-controlled bytecode-equivalent instructions during object reconstruction; a class implementing __reduce__ can force execution of an arbitrary callable (e.g., os.system) as soon as the “model” file is unpickled by the serving pipeline.
Attack Vector
- Attacker crafts a Python object whose
__reduce__method returns(os.system, (command,)). - The object is serialized with
pickle.dump()into a file that mimics a legitimate model artifact (e.g.,vulnerable_model.pkl). - The malicious artifact is delivered to, or substituted within, the model storage/tracking path used by the MLflow/MLServer deployment.
- When MLflow/MLServer loads the artifact via
pickle.load()(directly or throughmlflow.pyfuncinternals), the embedded command executes immediately inside the serving container.
Impact
Full arbitrary code execution within the model-serving container context, enabling data exfiltration, lateral movement, or persistence in ML infrastructure — with no authentication or user interaction required beyond the model being loaded.
Environment / Lab Setup
Target: Docker container running python:3.10-slim with mlflow==2.11.1, mlserver==1.3.5, mlserver-mlflow==1.3.5 (Dockerfile provided)
Attacker: Python 3.10 with pickle (stdlib) to craft the payload; docker cp / docker exec to stage and trigger it inside the sandbox
Proof of Concept
PoC Script
See
Dockerfile,docker-compose.yml,generate_model.py,trigger_native.py, andverify_poc.pyin this folder.
| |
generate_model.py builds a baseline MLflow pyfunc model inside the container. trigger_native.py serializes an ExploitModel whose __reduce__ hook calls os.system("touch /tmp/native_success_marker.txt"), then immediately unpickles it to simulate the server’s own loading path. verify_poc.py checks that the marker file was created, confirming code execution occurred during deserialization.
Detection & Indicators of Compromise
Signs of compromise:
- Model-serving process spawning shell or system binaries shortly after an artifact download/load event
- Unexplained files created in the container’s temp directory correlating with model load timestamps
- Model artifacts containing non-standard pickle opcodes or unexpected classes when inspected with
pickletools
Remediation
| Action | Detail |
|---|---|
| Primary fix | No vendor patch confirmed as of 2026-07-05 — monitor mlflow/mlserver advisories; migrate model formats away from pickle |
| Interim mitigation | Use Safetensors or ONNX for model storage instead of pickle/joblib/marshal; run serving containers as non-root with read-only filesystems and dropped capabilities; restrict artifact upload/storage access |
References
Notes
Mirrored from https://github.com/SparshBiswas-AI/CVE-2026-0596-Reproduction on 2026-07-05.
| |