YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
PoC: Zip-Slip arbitrary file write in PyTorch AOTIModelPackageLoader (.pt2) (CWE-22)
Loading a crafted PyTorch Export package (.pt2) via AOTIModelPackageLoader /
torch._inductor.package.load_package writes archive members to attacker-controlled paths outside the
extraction temp directory.
Root cause
torch/csrc/inductor/aoti_package/model_package_loader.cpp (load_pt2, ~839-882):
auto cur_filename = normalize_path_separator(zip_filename_str); // normalizes '\'<->'/' ONLY, not '..'
if (path_starts_with_directory(cur_filename, model_directory) || ...) { // PREFIX check only
std::string output_path_str = temp_dir_;
output_path_str += k_separator;
output_path_str += cur_filename; // temp_dir + '/' + attacker member name
...
zip_archive.extract_file(zip_filename_str, output_path_str); // writes; no '..' containment check
}
path_starts_with_directory only checks that the member name begins with data/aotinductor/<model>
(or data/constants). A member named data/aotinductor/<model>/../../../<path>/evil passes that check,
but the trailing ../ sequences make the concatenated output_path_str escape temp_dir_. extract_file
(miniz mz_zip_reader_extract_file_to_file โ fopen) then writes attacker bytes to an arbitrary location.
normalize_path_separator only swaps path separators; it does not remove ... No canonicalization /
containment (weakly_canonical + prefix check) is performed before the write.
Impact
Zip-slip arbitrary file write on the normal .pt2 load path โ overwrite/create files anywhere the process
can write (e.g. ~/.bashrc, cron, ssh authorized_keys), which can escalate to code execution.
Reproduce
g++ -g -O0 -std=c++17 torch_pt2_zipslip_harness.cpp -o poc && ./poc
# path_starts_with_directory(member, model_directory) = PASS (extracted)
# write target resolves to: /tmp/pt2_zipslip_demo/PWNED_OUTSIDE_TEMPDIR
# escaped temp_dir_? YES โ zip-slip arbitrary file write
# file exists outside temp_dir_: CONFIRMED
A real trigger is a .pt2 ZIP whose member name is data/aotinductor/<model>/../../../<target>.
Fix
After building output_path_str, canonicalize it (std::filesystem::weakly_canonical) and verify it is
still within temp_dir_ before extracting; reject any member whose resolved path escapes the temp dir.