| --- |
| license: mit |
| tags: |
| - security |
| - poc |
| --- |
| |
| # torch.package (PyTorch Package MFV) β picklescan/modelscan bypass PoC |
|
|
| Proof-of-concept model files for a huntr Model File Vulnerability (MFV) report against |
| the **PyTorch Package (.pt)** format (`torch.package`). |
|
|
| ## Files |
|
|
| - `malicious_interned.pt` β the actual vulnerability PoC. Runs `os.system("touch /tmp/torch_package_interned_pwned")` |
| on load via `torch.package.PackageImporter(...).load_pickle(...)`. Both `picklescan` (1.0.4) and |
| `modelscan` (0.8.8, made by Protect AI, huntr's parent org) report this file as **clean** |
| ("Infected files: 0" / "No issues found"). `modelscan --show-skipped` explicitly lists the payload |
| file (`archive/evilmodule.py`) as skipped: *"Model Scan did not scan file"*. |
| - `naive_detected_by_scanners.pt` β control/comparison file. Same RCE effect, but the pickle directly |
| references `subprocess.Popen`. Both scanners correctly flag this one as dangerous. Included to isolate |
| exactly which part of the technique causes the bypass (embedding the payload in the package's own |
| *interned* Python source module instead of referencing a known-dangerous stdlib global from the pickle |
| opcodes). |
|
|
| ## Why the scanners miss it |
|
|
| `torch.package` archives are zip files that can bundle "interned" `.py` source modules alongside the |
| pickle data. When such a module is imported (which routinely happens as part of unpickling a package |
| object), `torch/package/package_importer.py`'s `PackageImporter._make_module()` runs |
| `exec(code, ns)` on that source directly. The pickle opcode stream itself never references anything |
| scanners treat as dangerous (`os.system`, `subprocess.Popen`, `eval`, etc.) β it just references |
| a custom, package-local class. Opcode-level scanners (picklescan, modelscan) have no model of this |
| second execution path, so they see nothing to flag. |
|
|
| ## Reproduce |
|
|
| ```bash |
| pip install torch picklescan modelscan |
| python3 -c " |
| import torch.package |
| picklescan_result = __import__('subprocess').run(['picklescan', '-p', 'malicious_interned.pt']) |
| " |
| picklescan -p malicious_interned.pt # Infected files: 0 |
| modelscan -p malicious_interned.pt # No issues found! π |
| |
| python3 -c " |
| import torch.package |
| importer = torch.package.PackageImporter('malicious_interned.pt') |
| importer.load_pickle('archive', 'data.pkl') |
| " |
| ls -la /tmp/torch_package_interned_pwned # created by the RCE |
| ``` |
|
|
| Full report: huntr.com MFV submission (PyTorch Package format). |
|
|