Statically catching the CVE-2026-34445 ONNX external_data attribute-injection vector before the model loads
Made with AI: the scanner and this post were made with AI agents working for me, MV2 of Munim, Inc. An .onnx file looks like a safe thing to download. It is a protobuf message with no code of its own: no pickle, no Lamb
Made with AI: the scanner and this post were made with AI agents working for me, MV2 of Munim, Inc.
An .onnx file looks like a safe thing to download. It is a protobuf message with no code of its own: no pickle, no Lambda layer, nothing that runs. But a parser still has to read it, and in onnx before 1.21.0 one small field let a crafted file reach into the loader's own Python objects. This post explains that field, the advisory, and a free static check that flags it before any loader runs.
The field: external_data
Big models keep their weights outside the .onnx file. A tensor whose data_location is EXTERNAL names its weights file through a list of key/value string pairs in the tensor's external_data field. The ONNX spec recognises only four keys:
| key | meaning |
|---|---|
location |
path of the data file (required) |
offset |
byte offset where the tensor's data begins |
length |
number of bytes |
checksum |
SHA1 digest of the data file |
onnx >= 1.21.0 also keeps basepath, for save/load round-trips.
The bug: CVE-2026-34445
Before 1.21.0, onnx's ExternalDataInfo read those pairs by calling setattr(self, key, value) for every key, without validating it. So a crafted model carrying an unexpected key (a Python dunder such as __class__, or any other attribute name) could overwrite the object's internal state and crash a model-serving process at load time.
- Advisory: CVE-2026-34445 / GHSA-538c-55jv-c5g9
-
Affected:
onnx< 1.21.0 - Fixed: 1.21.0, by an allowlist of the recognised keys
- Class: CWE-20 (improper input validation), CWE-400, CWE-915
- Realistic impact: denial of service (CVSS 8.6 HIGH)
The check: onnx_external_data_key_scan.py
I shipped a free, Apache-2.0 scanner for exactly this vector, released as v0.13.0 of keras-model-scan. It uses only the Python standard library and never executes or loads the model:
- it parses the protobuf wire format itself, with no
onnxdependency; - it walks every tensor: graph initializers, sparse initializers, and tensors nested in node attributes and subgraphs (recursively);
- it flags any
external_datakey outside{location, offset, length, checksum, basepath}as OEK001 (HIGH), calling out dunder keys as attribute injection. A file it cannot parse as ONNX is reported as OEK000.
In CI, one line:
python onnx_external_data_key_scan.py ./model-dir --fail-on HIGH
The exit code is non-zero when a finding at or above --fail-on is present, so it works as a gate.
Why a separate tool? Existing model scanners such as ModelScan and ModelAudit check ONNX custom ops and external_data path traversal (location pointing outside the model directory), but as far as I can tell not unexpected external_data keys. So this appears to be the first standalone detector for this exact vector. If I have missed one, please tell me and I will correct this.
Plainly
- Every identifier here is already public. Nothing in this post is a zero-day.
- The scanner describes detection, not exploitation. It ships no attack file.
- The real fix is to upgrade
onnxto >= 1.21.0. The scan is a pre-load check for pipelines that accept third-party models, not a replacement for the upgrade. - A clean scan guarantees nothing. It means this one known vector was not found.
Scanner and full write-up: https://github.com/munimtechnologies/keras-model-scan/blob/main/ONNX_EXTERNAL_DATA_KEY_SCAN.md (repo: https://github.com/munimtechnologies/keras-model-scan). Issues and corrections are welcome.
MV2 of Munim, Inc.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.