An exploitable heap out of bounds write vulnerability exists in the UI tag parsing functionality of the DICOM image format of LEADTOOLS 20. A specially crafted DICOM image can cause an offset beyond the bounds of a heap allocation to be written, potentially resulting in code execution. An attacker can specially craft a DICOM image to trigger this vulnerability.
8.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CWE-787 - Out-of-bounds Write
LEADTOOLS, according to the website, “is a collection of comprehensive toolkits to integrate document, medical, multimedia, and imaging technologies into desktop, server, tablet, and mobile applications”. It offers prebuilt and portable libraries with an SDK for most platforms (Windows, Linux, Android, etc), that are all geared towards building applications for medical systems.
The module analyzed in this vulnerability is below:
Loaded symbol image file: C:\LEADTOOLS 20\Bin\CDLL\x64\ltdicx.dll Image path: C:\LEADTOOLS 20\Bin\CDLL\x64\ltdicx.dll Image name: ltdicx.dll Browse all global symbols functions data Timestamp: Fri Mar 1 14:52:51 2019 (5C799BA3) CheckSum: 0017C012 ImageSize: 00179000 File version: 220.127.116.11 Product version: 18.104.22.168
One toolkit provided by LEADTOOLS is a DICOM image parser. LEADTOOLS has a variety of prebuilt applications that leverage the DICOM library, including a direct image viewer and several DICOM servers.
Data in the DICOM format is formatted in a series of data structures. These data structures have various value representations (VR). Each VR describes the data type and format for that particular Data Element’s value. The UI tag (0x5549) includes a size field as well as the data immediately afterwards. An allocation occurs based on the size of the UI element.
ltdicx+4db48 .text:04DB48 8D 4D 01 lea ecx, [rbp+1] ; rcx - Size of UI element .text:04DB4B FF 15 77 8A 11 00 call L_LocalAlloc ;
After the buffer is allocated, during the handling of the UI tag, there is a check if a backslash (‘') exists in the data buffer. If there is a backslash, it is replaced with a null in order to find the length of the substring ending in the backslash. The backslash is then replaced in order to calculate the length of the full data buffer by looking for an ending null byte.
ltdicx+4e3b0 .text:04E3B0 mov edx, '\' .text:04E3B5 mov rcx, rsi ; Data buffer .text:04E3B8 call strchr ; Find '\' in the data buffer .text:04E3BD mov rdi, rax .text:04E3C0 test rax, rax .text:04E3C3 jz short loc_4E3C8 .text:04E3C5 mov byte ptr [rax], 0 ; Replace '\' with a null .text:04E3C8 mov rdx, rsi .text:04E3CB mov rcx, r13 .text:04E3CE call LDicomDS::DelSpaces ; Delete any spaces in the data buffer .text:04E3D3 test rdi, rdi .text:04E3D6 jz short loc_4E40C .text:04E3D8 mov byte ptr [rdi], '\' ; Return data buffer to original state .text:04E3DB mov rax, rbp .text:04E3DE inc rdi .text:04E3E1 cmp byte ptr [rdi+rax+1], 0 ; Find the length of the substring after '\' .text:04E3E6 lea rax, [rax+1] .text:04E3EA jnz short loc_4E3E1 .text:04E3EC lea r8d, [rax+1] ;  .text:04E3F0 mov rax, rbp .text:04E3F3 cmp byte ptr [rsi+rax+1], 0 ; Find the length of the data buffer .text:04E3F8 lea rax, [rax+1]
With the length of the data buffer and the length of the substring calculated, memmove is called to copy the second substring into the data buffer itself. The size of memmove is calculated and stored above .
ltdicx+4ed401 .text:04E401 mov rdx, rdi ; Second substring .text:04E404 add rcx, rsi ; Offset into the data buffer .text:04E407 call memmove
It is possible for an attacker to allocate a small enough buffer to hold the data after the memmove. The memmove will write data outside the bounds of the heap allocation, causing a heap based buffer overflow, potentially resulting in code execution.
ltdicx!L_DicomChannelDeleteAnnotation+0x83b9: 00007ffd`30260b19 0f1101 movups xmmword ptr [rcx],xmm0 ds:00000272`d71e9ffb=???????????????????????????????? >>> analyze -v MODULE_NAME: ltdicx IMAGE_NAME: ltdicx.dll DEBUG_FLR_IMAGE_TIMESTAMP: 5c799ba3 FAILURE_BUCKET_ID: INVALID_POINTER_WRITE_AVRF_c0000005_ltdicx.dll!L_DicomChannelDeleteAnnotation BUCKET_ID: APPLICATION_FAULT_INVALID_POINTER_WRITE_AVRF_ltdicx!L_DicomChannelDeleteAnnotation+83b9 FAILURE_EXCEPTION_CODE: c0000005
2019-08-08 - Vendor Disclosure
2019-09-06 - 30 day follow up with vendor re: case number assigned
2019-09-09 - Vendor acknowledged issue under review
2019-10-21 - 60+ day follow up
2019-12-05 - Vendor patched
2019-12-10 - Public Release
Discovered by Marcin Towalski and Cory Duplantis of Cisco Talos.