CoolFace
Modelpublic

tonydav41/torch-export-poc-pt2-aoti-zipslip-2026-05-12

sourceHugging Faceupdated 5mo agoView on Hugging Face
0likes
Model Card

Torch Export .pt2 AOTInductor Zip-Slip Path Traversal PoC

PoC for huntr submission against Torch Export (.pt2) – PyTorch.

Vulnerability

The AOTInductor .pt2 package loader (AOTIModelPackageLoader in torch/csrc/inductor/aoti_package/model_package_loader.cpp) performs a zip extraction with a simple prefix check (starts_with(cur_filename, model_directory)) and does not validate canonical containment of the destination path. A malicious archive member like:

aoti_zip_slip/data/aotinductor/model/../../../../../PT2_AUDIT.txt

passes the prefix check (because it starts with data/aotinductor/model) and is extracted to /tmp/PT2_AUDIT.txt during torch._inductor.package.load_package() / aoti_load_package().

Arbitrary file write outside the temp extraction directory.

  • —CWE-22 Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
  • —CWE-23 Relative Path Traversal
  • —ML multiplier eligible — payload is a .pt2 model file consumed by AOTInductor.

Affected version

  • —pytorch/pytorch master @ commit 44c2904880ec08f4ea80e794e2883fb731a3d87f (2026-05-12)
  • —Installed wheel: torch 2.11.0 (commit 70d99e998b4955e0049d13a98d77ae1b14db1f45)
  • —Vulnerable pattern present on both

Sinks

  • —torch/csrc/inductor/aoti_package/model_package_loader.cpp — AOTIModelPackageLoader extract loop uses starts_with(cur_filename, model_directory) plus extract_file(zip_filename_str, temp_dir + "/" + cur_filename) with no canonical-containment check
  • —Python wrapper: torch/_inductor/package/package.py falls back to the raw C++ loader after a benign-looking RuntimeError
  • —Public surface: torch._inductor.package.load_package(path, "model") and torch._inductor.aoti_load_package(path)

Reproducer

sh
python3 poc_aoti_zip_slip.py
cat /tmp/PT2_AUDIT.txt   # AOTI .pt2 ZIP SLIP SENTINEL

Observed output:

archive=/tmp/pt2-audit/aoti_zip_slip.pt2
entries:
  aoti_zip_slip/data/aotinductor/model/dummy.txt
  aoti_zip_slip/data/aotinductor/model/../../../../../PT2_AUDIT.txt
returncode=0
RuntimeError: Failed to find a generated cpp file or so file for model 'model' in the zip archive.
sentinel_exists True
AOTI .pt2 ZIP SLIP SENTINEL

The arbitrary write happens before the package is rejected for missing AOTInductor .cpp/.so artifacts.

Files

  • —aoti_zip_slip.pt2 — minimal malicious .pt2 (1,636 B)
  • —poc_aoti_zip_slip.py — reproducer harness
  • —poc_aoti_zip_slip.out — captured stdout/stderr

Distinguisher

  • —NOT torch.load() legacy .pt pickle issues
  • —NOT torch.package.PackageImporter
  • —NOT PyTorch Mobile .ptl Lite Interpreter (separate audit F-050)
  • —NOT TorchScript torch.jit.load
  • —NOT .mar archives

Distinct from a pending public huntr report titled "Zip Slip arbitrary file write via unsafe extractall() in torch.export.experimental..." — that report appears to target the Python torch.export.experimental extraction surface. This finding targets the C++ AOTIModelPackageLoader miniz extraction in torch._inductor.package / aoti_load_package. Different files, different APIs, different vulnerable code paths. Verified by GitHub-connector searches for AOTIModelPackageLoader path traversal, model_package_loader extract_file traversal — no matches.

Also distinct from known .pt2 pickle/torch.load duplicates ("RCE via torch.load weightsonly=False", "RCE in torch.export.load via Unsafe Deserialization", "weightsonly=False Silent Fallback in ExportedProgram Deserialization").

Recommended fix

In model_package_loader.cpp extraction loop, canonicalize the destination path before extracting and reject any member whose canonical path is not below the extraction root. Also reject archive member names containing .., absolute paths, or Windows drive prefixes. Require the prefix check to include a directory boundary (data/aotinductor/<model_name>/, not just data/aotinductor/<model_name>).

Reporter

Reported by lendtrain (huntr) / tonydav41 (HuggingFace). Filed 2026-05-12.