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.
This repository contains a proof-of-concept exploit payload for security research purposes. Access to the pickle payload and PoC code requires acknowledgement of responsible use. By requesting access, you confirm you are a security researcher, penetration tester, or student using this material for defensive/educational purposes only.
Log in or Sign Up to review the conditions and access this model content.
yaml.load + zlib β Two-Layer Deserialization Bypass
Security Research β Responsible Disclosure This repository is part of a systematic study of pickle scanner bypass techniques. The payload demonstrates a detection gap in ML model security scanners. Access to
malicious_model.pklandpoc.pyrequires a request β see gate above.
Summary
Scanner scores for malicious_model.pkl:
| Scanner | CRITICAL | WARNING | Bypassed? |
|---|---|---|---|
| modelaudit 0.2.37 | 0 | 3 | Yes β 0 CRITICAL |
| picklescan 1.0.4 | 0 | 0 | Yes |
| modelscan | 0 | 0 | Yes |
Vulnerability
Routes execution through PyYAML's unsafe loader β a second deserialization layer entirely outside the pickle scanner's analysis scope. The dangerous reference (posix.system) lives inside a zlib-compressed binary blob, invisible to both opcode-level analysis and raw byte pattern matching.
PyYAML's !!python/object/apply: tag calls arbitrary Python functions. Given:
!!python/object/apply:posix.system
- touch /tmp/poc
yaml.load(data, Loader=yaml.UnsafeLoader) calls posix.system("touch /tmp/poc"). PyYAML resolves the dotted name by splitting on ., importing posix, and calling system.
Proof of Concept
malicious_model.pkl in this repository is the exploit payload.
poc.py builds and tests it with full scanner output.
How the PoC was built
def build(cmd: str) -> bytes:
yaml_payload = f"!!python/object/apply:posix.system\n- {cmd}".encode()
compressed = zlib.compress(yaml_payload, level=9)
ops = bytearray(b"\x80\x04")
Affected file formats
Pickle (.pkl, .pt, .bin, .joblib) β any file deserialized via pickle.loads(),
torch.load(), joblib.load(), or equivalent.
Conditions required to trigger
- Target calls
pickle.loads(untrusted_bytes)or loads a model file via any pickle-based loader - The scanner performs static analysis only (no sandboxed execution)
- Scanner checks GLOBAL/STACK_GLOBAL opcodes against a deny list
Reproduction Steps
# 1. Request access above, then clone
git clone https://huggingface.co/SiggytheShark/pickle-bypass-yaml-zlib-deserialization
cd pickle-bypass-yaml-zlib-deserialization
# 2. Install requirements
pip install modelaudit picklescan modelscan
# 3. Scan β observe scanner scores match table above
modelaudit scan malicious_model.pkl
picklescan --path malicious_model.pkl
modelscan -p malicious_model.pkl
# 4. Execute to confirm RCE/side-effect
python3 poc.py
# 5. Verify
ls /tmp/scanner_bypass_proof.txt
Security Impact
- Add
"yaml"toALWAYS_DANGEROUS_MODULES - Add decompression functions (
zlib.decompress,gzip.decompress, etc.) toALWAYS_DANGEROUS_FUNCTIONS - Attempt decompression of binary blobs for recursive scanning
- Close S205 dummy-REDUCE bypass by checking if REDUCE result is immediately POPped then class retrieved from memo
Bypass mechanism: The payload evades static analysis while achieving its effect
(code execution, file write, or network connection) when pickle.loads() is called.
Real-world scenario: An attacker uploads this payload to a model hub. A victim
downloads and loads it. The scanner reports the file as safe (0 CRITICAL). The
payload fires silently β the return value of pickle.loads() looks like a normal
Python object while the side effect has already occurred.
Full Technical Writeup
yaml.load + zlib β Two-Layer Deserialization Bypass
Score: 0 CRITICAL, 3 WARNING
Also bypasses: ClamAV daily signatures (daily.cvd v27970)
Technique: Second-layer deserialization with compressed payload
Scanner version: modelaudit 0.2.37
Mechanism
Routes execution through PyYAML's unsafe loader β a second deserialization layer entirely outside the pickle scanner's analysis scope. The dangerous reference (posix.system) lives inside a zlib-compressed binary blob, invisible to both opcode-level analysis and raw byte pattern matching.
PyYAML's !!python/object/apply: tag calls arbitrary Python functions. Given:
!!python/object/apply:posix.system
- touch /tmp/poc
yaml.load(data, Loader=yaml.UnsafeLoader) calls posix.system("touch /tmp/poc"). PyYAML resolves the dotted name by splitting on ., importing posix, and calling system.
Four-Layer Evasion
| Layer | What's bypassed | How |
|---|---|---|
| 1 | modelaudit deny-list | yaml, zlib absent from all ban lists β 3Γ S201 WARNING |
| 2 | Raw byte / pattern detection | posix.system inside compressed blob β no readable ASCII |
| 3 | ClamAV signatures | Covers yaml.unsafe_load but NOT yaml.load + yaml.UnsafeLoader split |
| 4 | S205 import-only check | Dummy UnsafeLoader(b'') REDUCE + POP satisfies "used as callable" requirement |
Pickle Structure
yaml.UnsafeLoader β memoized + dummy REDUCE + POP [WARNING β bypasses S205]
yaml.load( [WARNING 1]
zlib.decompress(<91 bytes of binary>), [WARNING 2]
UnsafeLoader_from_memo [no new GLOBAL]
) β yaml.load fires β posix.system β RCE
Strings visible in file: only yaml, UnsafeLoader, zlib, decompress. The strings posix, system, os, unsafe_load, python/object/apply never appear.
ClamAV Signature Gap
ClamAV's Py.Malware.Obfuscation_yaml_unsafe_load_G and _SG signatures match the literal byte sequence for yaml.unsafe_load. Using yaml.load + yaml.UnsafeLoader as separate references produces neither pattern. Confirmed by extracting and searching full daily.ldb (v27970).
PyYAML as the Highest-Value Second-Layer Deserializer
PyYAML is a transitive dependency of virtually every ML stack (PyTorch, transformers, sklearn, huggingface_hub) and is absent from all scanner deny lists. It supports arbitrary Python object construction via !!python/object/apply:.
Recommended Fixes
- Add
"yaml"toALWAYS_DANGEROUS_MODULES - Add decompression functions (
zlib.decompress,gzip.decompress, etc.) toALWAYS_DANGEROUS_FUNCTIONS - Attempt decompression of binary blobs for recursive scanning
- Close S205 dummy-REDUCE bypass by checking if REDUCE result is immediately POPped then class retrieved from memo
Requirements
pip install pyyaml
General Analysis β Security Research