Picklescan, the de facto security gate for ML pipelines against malicious pickle files, has a trivial bypass via Python's built-in distutils module — meaning any organization running picklescan < 0.0.33 has a false sense of protection. Malicious models crafted with distutils payloads pass scanning clean and can overwrite SSH authorized_keys, web configs, or source code on load, escalating to full RCE. Patch to 0.0.33 immediately and audit all pickle files ingested while running the vulnerable version.
Affected Systems
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| picklescan | pip | < 0.0.33 | 0.0.33 |
Do you use picklescan? You're affected.
Severity & Risk
Recommended Action
- 1) Upgrade picklescan to 0.0.33 immediately: pip install --upgrade picklescan. 2) If immediate patching is blocked, add 'distutils' to your custom blocklist config as a temporary workaround. 3) Audit file modification logs for unexpected writes to sensitive paths (authorized_keys, web configs, app source dirs) from Python processes during the vulnerable window. 4) Apply defense-in-depth regardless of patch status: treat pickle files as untrusted executables; prefer safetensors or ONNX for model distribution wherever possible. 5) Detection: alert on Python processes writing to /root/.ssh/, /home/*/.ssh/, web server config dirs, or application source directories.
Classification
Compliance Impact
This CVE is relevant to:
Technical Details
NVD Description
### Summary Picklescan has got open() and shutil in its default dangerous blocklist to prevent arbitrary file overwrites. However the module distutils isnt blocked and can be used for the same purpose ie to write arbitrary files. ### Details This is another vulnerability which impacts the downstream user. By constructing a pickle that user distutils.file_util.write_file, an attacker can overwrite critical system files (like .ssh/authorized_keys, web server configurations, or source code) to achieve DoS or escalate to RCE. ### PoC ```python import pickle import distutils.file_util class FileWriteBypass: def __reduce__(self): target_file = "pwned_config.env" content = ["print('I have overwritten your config')"] return (distutils.file_util.write_file, (target_file, content)) payload = pickle.dumps(FileWriteBypass()) with open("bypass_filewrite.pkl", "wb") as f: f.write(payload) print("bypass_filewrite.pkl") ``` <img width="853" height="197" alt="image" src="https://github.com/user-attachments/assets/a129f5aa-a050-4e88-adb7-5a6f93e35b65" /> To fix this just add disutil to the blacklist
Exploitation Scenario
An adversary publishes a malicious model (e.g., fine-tuned-llm.pkl) to a public model hub or compromises a private model registry entry. The model contains a distutils.file_util.write_file payload targeting /root/.ssh/authorized_keys, inserting the attacker's public SSH key. The victim's MLOps CI/CD pipeline scans the model with picklescan < 0.0.33 — scan returns clean, model is promoted to the registry. On the next training run or inference server startup, the payload fires silently during model deserialization, writes the SSH key, and establishes persistent root access to the ML host. No post-exploitation tooling required; the initial foothold is fully established through the model file alone.