Unsafe Deserialization
You publish a pretrained model / config artifact others load. The loader runs a source scanner that rejects literal eval(, exec(, and os.system( — then it deserializes the artifact with pickle / torch.load / yaml.load.
Craft an artifact that runs code on load without any banned literal — using an unsafe deserialization sink (a __reduce__ method, a PyYAML !!python/object/apply tag, or a pickle GLOBAL opcode).
No leads yet. Declassify intel one step at a time when you’re stuck.
How this attack works
Deserializing untrusted data is code execution. pickle faithfully restores a __reduce__ result by calling the callable it names; PyYAML’s !!python/object/apply does the same. The source scanner never saw eval or os.system because the call is assembled at load time, not written in the file.
Why it's dangerous
AI pipelines routinely torch.load / unpickle model checkpoints downloaded from hubs and registries. A single poisoned .pkl or .pt file runs attacker code in the training or inference environment. SkillTotal flags unsafe deserializers as ST-DESERIALIZE-PY.
OWASP mapping
Maps to OWASP Top 10 for LLM Applications (2025): LLM03: Supply Chain (malicious model artifacts) and OWASP Agentic Skills AST05: Unsafe Deserialization. SkillTotal flags the sink as ST-DESERIALIZE-PY.
How to defend
- Never unpickle untrusted data; prefer safe formats (safetensors, JSON, protobuf).
- Use
yaml.safe_load— neveryaml.loadwith a full loader. - Verify artifact provenance and integrity (signatures/hashes) before loading.
- Load untrusted artifacts only in a sandbox with no network and least privilege.
SkillTotal catches this class of issue deterministically (rule ST-DESERIALIZE-PY).
FAQ
- Why doesn't scanning for eval/exec/os.system work?
- Deserialization builds the call at load time from data, not source. The file contains a __reduce__ result or a YAML tag — no banned literal — yet the unpickler executes it.
- How does SkillTotal detect this?
- ST-DESERIALIZE-PY flags use of unsafe deserializers (pickle.loads, torch.load, yaml.load, etc.) on untrusted input — the dangerous sink — without executing anything.