YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
modelscan-pytorch-zip-gadget-chain-bypass
This repository contains a proof of concept for a scanner bypass in modern ZIP-based PyTorch .pt model files.
Vulnerability
The open-source protectai/modelscan scanner correctly opens ZIP-based PyTorch archives and scans the embedded data.pkl pickle. However, it still relies on a static unsafe-global blacklist, which can be bypassed by a higher-order gadget chain whose imported globals look benign.
The included payload uses only:
operator.methodcallerimportlib.import_module
At deserialization time, those globals compose into the equivalent of:
operator.methodcaller("system", "touch PT_MARKER.txt")(importlib.import_module("os"))
This means torch.load(..., weights_only=False) executes the payload even though modelscan reports zero issues and zero errors.
Primary PoC
The main PoC file is:
artifacts/gadget_zip_pytorch.pt
The payload is harmless. When loaded through the unsafe path, it creates a local marker file:
PT_MARKER.txt
Included Files
artifacts/gadget_zip_pytorch.ptartifacts/reproduce_output.txtartifacts/sha256sums.txtreproduce.py
Tested Environment
- Date tested: July 16, 2026
- Python: 3.13.12
- PyTorch: 2.13.0+cpu
modelscan:protectai/modelscan@61fcec9c2a37c24c1fb12d84ede30fe248a364bd
Reproduction
1. Verify the included PoC
Run:
python reproduce.py --modelscan-path /path/to/modelscan
If modelscan is already installed in your environment, --modelscan-path is optional.
Expected results:
modelscanreturns zero issues and zero errorstorch.load(..., weights_only=False)loads successfully- the payload creates
PT_MARKER.txt - the returned object is still an
OrderedDict
2. Rebuild the artifact from a clean ZIP-based .pt template
Run:
python reproduce.py --build --template-pt /path/to/clean_zip_pytorch.pt --modelscan-path /path/to/modelscan
The clean template must be a ZIP-based PyTorch archive with a data.pkl member. This regenerates artifacts/gadget_zip_pytorch.pt and reruns the verification steps.
Important Nuance
Recent PyTorch versions changed the default torch.load behavior to weights_only=True, which blocks unsafe globals earlier in the load pipeline.
The vulnerable / unsafe path demonstrated here is:
torch.load(path, weights_only=False)
That is the deserialization-equivalent path relevant to file-format ACE discussions and to environments that explicitly disable the safer default or use older behavior.
Observed Results
From artifacts/reproduce_output.txt:
modelscan_issue_count: 0modelscan_errors: []torch_load_type: OrderedDicttorch_load_keys: []marker_exists_after_torch_load: True
Impact
This is a practical semantic scanner bypass for a real PyTorch archive format:
- the file is a valid ZIP-based
.ptmodel artifact modelscaninspects the embeddeddata.pkl- the imported globals look benign under the current blacklist policy
- the unsafe PyTorch load path still reaches code execution
Notes
- The included artifact is intentionally harmless and only creates a local marker file.
- This bypass does not depend on malformed ZIP structure or parse errors.