CVE-2026-94093 Overview
CVE-2026-94093 is an insecure deserialization vulnerability in DLR-RM stable-baselines3 up to version 2.9.0. The flaw affects the PPO.load, load_replay_buffer, and VecNormalize.load functions defined in save_util.py. Loading an attacker-controlled model or replay buffer file triggers unsafe pickle deserialization, allowing arbitrary object instantiation during the load operation. The issue is classified under [CWE-20] Improper Input Validation and can be triggered remotely when a victim loads a malicious artifact. A public disclosure exists, and hardening previously applied via weights_only=True was reverted on master through PR #1913 for PyTorch 1.13 compatibility.
Critical Impact
A user who loads a crafted stable-baselines3 model, replay buffer, or VecNormalize file can execute attacker-supplied Python code inside the training or inference process.
Affected Products
- DLR-RM stable-baselines3 versions up to and including 2.9.0
- Core load APIs: PPO.load, load_replay_buffer, VecNormalize.load in save_util.py
- Downstream projects that consume stable-baselines3 model artifacts from untrusted sources
Discovery Timeline
- 2026-09-20 - CVE-2026-94093 published to the National Vulnerability Database
- 2026-09-21 - Last updated in NVD database
Technical Details for CVE-2026-94093
Vulnerability Analysis
The stable-baselines3 library serializes model state, replay buffers, and VecNormalize statistics using Python pickle inside ZIP archives. The load routines in save_util.py invoke pickle.load on file contents without integrity checks or a safe loader. Any Python object graph embedded in the artifact is reconstructed during deserialization, which executes __reduce__ methods supplied by the attacker.
In version 2.9.0, only the PyTorch tensor path was hardened using torch.load(..., weights_only=True). That change was reverted on master through PR #1913 to restore PyTorch 1.13 compatibility. As a result, none of the three public entry points enforce a safe-mode load, and the outstanding hardening tracked in PR #2264 has not yet shipped.
Root Cause
The root cause is unsafe deserialization of untrusted input in save_util.py. The load functions do not validate object types, restrict allowed classes, or require a signed artifact. Because pickle deserialization is Turing-complete, any callable reachable from the interpreter can be invoked during the load call.
Attack Vector
An attacker publishes a malicious .zip model, replay buffer, or VecNormalize file to a model registry, Hugging Face-style hub, shared storage, or a phishing lure. When a data scientist or automated pipeline calls PPO.load(path), load_replay_buffer(path), or VecNormalize.load(path), the embedded pickle payload executes with the privileges of the loading process. Exploitation requires user interaction to trigger the load call, and the disclosure notes the exploit has been made public.
// No verified proof-of-concept code is published in the enriched data.
// See the GitHub issue and VulDB entries linked below for technical details.
Detection Methods for CVE-2026-94093
Indicators of Compromise
- Unexpected child processes spawned by Python interpreters running training or inference jobs that call stable-baselines3 load APIs
- Outbound network connections initiated immediately after a PPO.load, load_replay_buffer, or VecNormalize.load invocation
- stable-baselines3 model archives sourced from untrusted repositories, mirrors, or user uploads
- ZIP artifacts containing pickle streams with references to os, subprocess, builtins.eval, or builtins.exec
Detection Strategies
- Statically scan model archives with tools such as picklescan or fickling before any load operation
- Instrument Python environments to log module imports and process creation during torch.load and pickle.load calls
- Alert on stable-baselines3 processes writing to disk outside model or log directories, or modifying user startup files
Monitoring Recommendations
- Monitor MLOps runners and Jupyter hosts for anomalous process trees originating from python workers that load third-party models
- Track file ingress into model directories and correlate with the identity that published the artifact
- Capture egress network telemetry from training clusters and flag first-seen destinations following a model load
How to Mitigate CVE-2026-94093
Immediate Actions Required
- Treat all stable-baselines3 model, replay buffer, and VecNormalize files as untrusted code until a fixed release is available
- Restrict loading to artifacts produced internally or cryptographically signed by a known publisher
- Isolate model-loading workloads in sandboxed containers with no outbound network access and least-privilege service accounts
Patch Information
No fixed release is identified in the enriched data. The upstream hardening effort is tracked in pull request #2264 against the DLR-RM stable-baselines3 repository. Additional context is available in the GitHub Issue #2281 discussion and the VulDB CVE-2026-94093 entry.
Workarounds
- Pre-scan model archives with picklescan or fickling and reject artifacts containing dangerous opcodes before invoking any load API
- Load models inside ephemeral containers or virtual machines with no credentials, secrets, or network egress
- Maintain an allowlist of trusted model sources and verify checksums or signatures before ingestion
- Where feasible, load only the tensor weights via a hardened torch.load(..., weights_only=True) path and reconstruct the policy from code
# Example pre-load scan using picklescan
pip install picklescan
picklescan --path ./downloaded_model.zip
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.