Downloads · 30 days
0
drogba771/modelscan-pickle-bypass-poc
modelscan-pickle-bypass-poc is a machine learning model from drogba771. Use it for the machine learning task on the model card, and read the license before you ship it in a product.
Downloads · 30 days
0
Access
Public
Updated Jul 12, 2026
Repo size
277 B
Likes
0
Public
Click a slice to open those files.
.md5.9 KB · 77%
From the Hugging Face model README
ModelScan 0.8.8 is Protect AI's scanner for detecting unsafe operations in ML model files. It flags pickle files containing dangerous GLOBAL imports (e.g., os.system, subprocess.Popen) by analyzing pickle opcodes via pickletools.genops().
This report demonstrates a complete bypass of ModelScan 0.8.8's pickle scanner. The technique achieves arbitrary code execution via pickle.load() while producing zero ModelScan issues.
Status: ✅ ModelScan bypass confirmed | ✅ RCE confirmed
Python's pickle module is inherently unsafe — pickle.load() can execute arbitrary code by design. ModelScan attempts to detect this by scanning pickle opcodes for "unsafe" module imports.
modelscan/settings.py)| Severity | Blocked Modules/Functions |
|---|---|
| CRITICAL | __builtin__.{eval,compile,getattr,apply,exec,open,breakpoint,__import__}, builtins.{eval,...}, os*, nt*, posix*, subprocess*, sys*, socket*, pty*, pickle*, _pickle*, shutil*, asyncio*, runpy*, bdb*, pdb*, operator.attrgetter |
| HIGH | webbrowser*, httplib*, requests.api*, aiohttp.client* |
The following stdlib modules can be used for code execution but are completely absent from ModelScan's blocklist:
marshal — marshal.loads() deserializes Python code objectstypes — types.FunctionType() creates executable functions from code objectsctypes — ctypes.CDLL(None).system() calls libc directlyimportlib — importlib.import_module('os') loads any module at runtimeselfcontained_bypass_277.pkl| Metric | Value |
|---|---|
| Size | 277 bytes |
| SHA256 | 9bc06071f234c0ddfe96ca9b43ecd873516492cbf99ae2ab835fbbbf2a6a2f80 |
| pickle.load() RCE | ✅ Confirmed |
| ModelScan 0.8.8 | ✅ 0 issues (0 critical, 0 high, 0 medium, 0 low) |
| Dependencies | None — fully self-contained |
# RCE test (no dependencies)
python3.11 -c "import pickle; pickle.load(open('selfcontained_bypass_277.pkl','rb'))"
cat /tmp/huntr_bypass_poc # → BYPASS_OK
# ModelScan bypass verification
modelscan scan -p selfcontained_bypass_277.pkl -r json
# → "total_issues": 0, "issues": []
Tested environment: CPython 3.11.15 + ModelScan 0.8.8 Note: marshal-based code objects are CPython minor-version specific; regenerate using
build_selfcontained_pickle.pyfor the target Python minor version.
The pickle uses only 2 GLOBAL imports, neither blocked:
| Opcode | Import | In ModelScan blocklist? |
|---|---|---|
GLOBAL types FunctionType | types.FunctionType | ❌ NOT BLOCKED |
GLOBAL marshal loads | marshal.loads | ❌ NOT BLOCKED |
Execution chain:
pickle.load()
→ marshal.loads(embedded_code_bytes) # NOT scanned
→ types.FunctionType(code_obj, {}) # NOT scanned
→ function() → import os → os.system() # Runtime, invisible to scanner
The embedded code object (232 bytes) contains a compiled function that imports os and calls os.system() at runtime — ModelScan never sees these opcodes because they don't exist in the pickle stream.
All confirmed 0 issues on ModelScan 0.8.8:
| Variant | Size | Technique |
|---|---|---|
marshal+types | 291 B | marshal.loads → types.FunctionType |
ctypes | 115 B | ctypes.CDLL(None).system() |
importlib | 118 B | importlib.import_module('os') |
class_traversal | 124 B | ().__class__.__bases__[0].__subclasses__() → os.system |
modelscan>=0.8.0 (optional, for verification)python3.11 -c "import pickle; pickle.load(open('selfcontained_bypass_277.pkl','rb'))"
cat /tmp/huntr_bypass_poc # → BYPASS_OK
modelscan scan -p selfcontained_bypass_277.pkl -r json
# Expected: "total_issues": 0
python3.11 build_selfcontained_pickle.py
# Builds the 277-byte payload from source code (CPython version-specific)
| File | Size | Description |
|---|---|---|
selfcontained_bypass_277.pkl | 277 B | ⭐ Golden PoC — self-contained pickle, 0 deps, ModelScan bypass |
build_selfcontained_pickle.py | 7.5 KB | Builder script (reproduces golden PoC) |
README.md | This file | |
pickles/pickle_bypass_marshal.pkl | 291 B | marshal+types variant |
pickles/pickle_bypass_ctypes.pkl | 115 B | ctypes variant |
pickles/pickle_bypass_importlib.pkl | 118 B | importlib variant |
pickles/pickle_bypass_class_traversal.pkl | 124 B | class_traversal variant |
pickle.load(), joblib.load(), or torch.load() on untrusted files is vulnerablectypes, importlib, builtins (partially).safetensors, ONNX, or signed picklesmarshal, types, ctypes, importlib to ModelScan's blocklist