--- license: openrail tags: - security - adversarial - pickle - model-scanner-bypass - red-team extra_gated_prompt: > 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. extra_gated_fields: Organization: text Intended use: text I agree to use this only for defensive security research: checkbox --- # imp.load_source — Two-Step Module Execution > **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.pkl` and `poc.py` requires 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 `imp.load_source(name, pathname)` reads and executes a Python source file from an explicit path. This is the legacy way to load a module from a file, predating `importlib`. Combined with `pathlib.Path.write_text`, it enables immediate code execution without needing the CPython internal `_load` function. --- ## 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 ```python def build(canary: str, tmp_py: str) -> bytes: content = f'import os; os.system("echo imp_load_source_pwned > {canary}")\n' rot13_content = codecs.encode(content, "rot_13") 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 1. Target calls `pickle.loads(untrusted_bytes)` or loads a model file via any pickle-based loader 2. The scanner performs **static analysis only** (no sandboxed execution) 3. Scanner checks GLOBAL/STACK_GLOBAL opcodes against a deny list --- ## Reproduction Steps ```bash # 1. Request access above, then clone git clone https://huggingface.co/SiggytheShark/pickle-bypass-imp-load-source cd pickle-bypass-imp-load-source # 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 `"imp"` to `ALWAYS_DANGEROUS_MODULES`. The entire `imp` module is deprecated and has no legitimate use in model pickles. **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 # imp.load_source — Two-Step Module Execution **Score:** 0 CRITICAL, 3 WARNING **Technique:** Write Python source to /tmp/, then imp.load_source to execute it **Scanner version:** modelaudit 0.2.37 **Python version:** ≤ 3.11 only (imp removed in 3.12) ## Mechanism `imp.load_source(name, pathname)` reads and executes a Python source file from an explicit path. This is the legacy way to load a module from a file, predating `importlib`. Combined with `pathlib.Path.write_text`, it enables immediate code execution without needing the CPython internal `_load` function. ## Pickle Structure (Two-Step) ``` _codecs.encode(rot13_content, 'rot_13') → source content [0 findings] Step 1 — Write source: pathlib.Path(tmp_py) → path object [WARNING 1] pathlib.Path.write_text(path, content) → file written [WARNING 2] Step 2 — Execute: imp.load_source('_poc', tmp_py) → executes source [WARNING 3] ``` ## Comparison with CPython Internals Strategy | | `03_cpython_tmp_import` | `26_imp_load_source` | |---|---|---| | Write function | `_frozen_importlib_external._write_atomic` | `pathlib.Path.write_text` | | Execute function | `_frozen_importlib._load` | `imp.load_source` | | Warnings | 4W | 3W | | Python version | All | ≤ 3.11 | ## Recommended Fix Add `"imp"` to `ALWAYS_DANGEROUS_MODULES`. The entire `imp` module is deprecated and has no legitimate use in model pickles. --- *General Analysis — Security Research*