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.

Log in or Sign Up to review the conditions and access this model content.

YAML Metadata Warning:empty or missing yaml metadata in repo card

Check out the documentation for more information.

modelscan blind to PyTorch .pt2 (PT2-Archive): torch.export.load() unpickles an attacker payload (weights_only=False, gated by an in-archive JSON flag) while modelscan reports 0 issues

Severity: Critical (silent RCE on the load of an untrusted .pt2, past modelscan supply-chain gating) Affected tool: modelscan 0.8.8 (coverage gap β€” does not scan .pt2). Loader: torch.export.load / torch.export.pt2_archive.load_pt2 (torch 2.12.0). Category: ModelScan coverage-gap on a new $4k-class format β€” .pt2 is PyTorch's current, actively-promoted export format.

Summary

.pt2 (PT2-Archive) is PyTorch's recommended export format (torch.export.save/load), a ZIP written by PyTorchFileWriter. On load, torch.export.load() β†’ load_pt2() β†’ _load_state_dict()/_load_constants() calls torch.load(io.BytesIO(payload_bytes), weights_only=False) with weights_only hardcoded to False (it does not inherit torch.load's safe default). Whether an entry is unpickled vs. treated as a raw tensor is decided by the boolean use_pickle field in a per-model JSON config stored inside the archive (model_weights_config.json) β€” fully attacker-controlled. An attacker takes any benign .pt2, flips one entry to use_pickle=true / tensor_meta=null, and replaces that entry's extension-less payload file (data/weights/weight_0) with a torch.save(Evil()) blob whose __reduce__ returns os.system(...) β†’ arbitrary code execution on load.

modelscan is blind to this: .pt2 is not in modelscan's supported_zip_extensions ([.zip,.npz]) or any scanner's extensions. modelscan does recurse the .pt2 (zip by magic), but the inner payloads are named weight_0/constant_0 with no extension, and FormatViaExtensionMiddleware assigns a scanner purely by suffix β†’ empty suffix β†’ no scanner runs β†’ every entry SCAN_NOT_SUPPORTED. Net: the documented loader executes the payload while the flagship scanner reports 0 issues.

Root cause

  • RCE sink (loader): torch/export/pt2_archive/_package.py β€” _load_state_dict() lines 877-878 (torch.load(BytesIO(weight_bytes), weights_only=False)); _load_constants() 935-936 (same); custom-obj branch line 958 (torch._C._pickle_load_obj β€” raw native pickle, source-confirmed 3rd sink, not weaponized). Gate: use_pickle flag from attacker-controlled JSON via _load_payload_config (line 842) / _build_file_map (818).
  • Scanner gap (huntr target): modelscan/settings.py supported_zip_extensions=[".zip",".npz"] (line 30); pickle/pytorch supported_extensions (59-73) and FormatViaExtension formats (78-91) all omit .pt2. modelscan/middlewares/format_via_extension.py:8 assigns format purely by suffix.

Reproduce

python poc/poc_pt2_rce.py then poc/poc_modelscan_gap.py (env: torch 2.12.0+cpu, modelscan 0.8.8, Python 3.12; verified):

  • RCE: torch.export.load(malicious.pt2) wrote PWNED_pt2_load.txt (os.system executed during the hardcoded weights_only=False unpickle; only a benign downstream SpecViolationError follows, after code execution). Inner archive/data/weights/weight_0 confirmed suffix=''.
  • Scanner gap: ModelScan().scan(malicious.pt2) β†’ total_issues=0, all severities 0, total_scanned=0, total_skipped=9 β€” every entry SCAN_NOT_SUPPORTED; the malicious payload weight_0 is not even enumerated.

Impact

ACE on the host when a victim calls torch.export.load() on an attacker-supplied .pt2. .pt2 is PyTorch's actively-promoted replacement for older export workflows, so this loader is increasingly used in MLOps/CI/model-hub pipelines β€” and because modelscan returns 0 issues, a malicious .pt2 passes automated supply-chain gating silently. Remediation: modelscan should add .pt2 to supported zip/scanner formats and scan inner pickle payloads by content/magic, not by extension.

Dup-check & honest framing

Clear of public prior art. The torch.load weights_only=True bypass family (CVE-2025-32434, CVE-2026-24747, GHSA-53q9-r3pm-6pq6) is the opposite mechanism (defeating the safe unpickler); here the .pt2 loader hardcodes weights_only=False (no safe mode to bypass) and is gated by an in-archive JSON flag. No public source describes torch.export.load/.pt2/load_pt2 as a pickle RCE sink or modelscan's .pt2 blind spot. Distinct from our R2 torch.package and R3 torchscript (different ZIP layout, loader path, and gate).

Filing guidance (honest): PyTorch may treat the loader side as by-design (torch.export.load carries a generic "never load untrusted data" warning), so file this primarily as the modelscan .pt2 coverage-gap (the scanner silently passes a code-executing model in PyTorch's current export format) β€” that is the bounty-relevant, undocumented vulnerability. The specific use_pickle in-archive payload vector strengthens it. Belongs in the "scanner β‰  loader / coverage-gap" banker cluster.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support