manja316's picture
Upload folder using huggingface_hub
11a7366 verified

modelscan Bypass PoC: distutils.spawn.spawn()

Vulnerability

modelscan 0.7.6 fails to detect malicious pickle files that use distutils.spawn.spawn() for command execution. The distutils.spawn module is not included in modelscan's unsafe_globals blocklist.

Attack Chain

Minimal single-REDUCE chain:

  1. STACK_GLOBAL "distutils.spawn" "spawn" — loads the spawn function
  2. Build argument list ["sh", "-c", "arbitrary_command"]
  3. REDUCE — calls spawn(["sh", "-c", "arbitrary_command"])

Impact

  • Full RCE via pickle.load() or torch.load()
  • Bypasses modelscan 0.7.6 completely — reports "No issues found"
  • Extremely simple chain — single function call, no chaining needed
  • distutils.spawn.spawn() internally calls os.spawnv() but this is invisible to modelscan's static opcode analysis

Reproduction

python create_poc.py
modelscan scan -p pytorch_model.bin
# Output: "No issues found"

python -c "import pickle; pickle.loads(open('pytorch_model.bin','rb').read())"
# Creates /tmp/distutils_bypass_proof

Root Cause

modelscan only checks top-level module references in pickle GLOBAL/STACK_GLOBAL opcodes against its blocklist. distutils.spawn is not listed. The internal delegation to os.spawnv() happens at Python runtime, not in pickle bytecode.

Fix Recommendation

Add "distutils.spawn": "*" to unsafe_globals.CRITICAL in modelscan/settings.py.