Talos Vulnerability Report

TALOS-2019-0892

LEADTOOLS BMP Parsing Remote Code Execution Vulnerability

November 5, 2019
CVE Number

CVE-2019-5100

Summary

An exploitable integer overflow vulnerability exists in the BMP header parsing functionality of LEADTOOLS 20. A specially crafted BMP image file can cause an integer overflow, potentially resulting in code execution. An attacker can specially craft a BMP image to trigger this vulnerability.

Tested Versions

LEADTOOLS 20.0.2019.3.15

Product URLs

https://www.leadtools.com/

CVSSv3 Score

8.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

CWE

CWE-190: Integer Overflow or Wraparound

Details

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 used for this analysis is below:

Loaded symbol image file: lfBmpX.DLL
Mapped memory image file: C:\LEADTOOLS 20\Bin\CDLL\x64\lfBmpX.DLL
Image path: C:\LEADTOOLS 20\Bin\CDLL\x64\lfBmpX.DLL
Image name: lfBmpX.DLL
Browse all global symbols  functions  data
Timestamp:        Thu Feb 21 13:22:56 2019 (5C6EFA90)
CheckSum:         0001C9A0
ImageSize:        00014000
File version:     20.0.0.1
Product version:  20.0.0.0
File flags:       0 (Mask 3F)
File OS:          40004 NT Win32
File type:        2.0 Dll
File date:        00000000.00000000
Translations:     0409.04e4

LEADTOOLS begins parsing BMP files by checking if the two signature bytes are AM or BM.

lfbmpx+2745
.text:0000000000002745                 lea     rdx, [rsp+550h+var_508]
.text:000000000000274A                 mov     r8d, 2
.text:0000000000002750                 mov     rcx, rax
.text:0000000000002753                 mov     [rsp+550h+arg_10], r12
.text:000000000000275B                 call    cs:L_RedirectedRead
.text:0000000000002761                 cmp     eax, 2
.text:0000000000002764                 jnz     loc_30B8
.text:000000000000276A                 movzx   eax, [rsp+550h+var_508]
.text:000000000000276F                 mov     r12d, 'MB' ; Possible signature bytes
.text:0000000000002775                 mov     ebx, 'AB'  ; Possible signature bytes
.text:000000000000277A                 cmp     ax, r12w
.text:000000000000277E                 jz      short loc_278E
.text:0000000000002780                 cmp     ax, bx
.text:0000000000002783                 jz      short loc_278E
.text:0000000000002785                 test    ax, ax
.text:0000000000002788                 jnz     loc_30B8

Assuming the file has the correct signature, the parser proceeds to read the BITMAPINFOHEADER, which includes the biWidth field.

lfbmpx+28fb
.text:00000000000028FB                 lea     rdx, [rsp+550h+var_4F0]
.text:0000000000002900                 mov     r8d, 28h
.text:0000000000002906                 mov     rcx, rdi
.text:0000000000002909                 call    cs:L_RedirectedRead ; Internal read
.text:000000000000290F                 cmp     eax, 28h
.text:0000000000002912                 jnz     loc_2E8E        
.text:0000000000002918                 cmp     dword ptr [rsp+550h+var_4F0], eax
.text:000000000000291C                 jnz     loc_2E8E        
.text:0000000000002922                 mov     r8d, dword ptr [rsp+550h+var_4F0+4]
.text:0000000000002927                 movzx   r15d, word ptr [rsp+550h+var_4E8+6]
.text:000000000000292D                 mov     [rbp+450h+var_4C8], eax
.text:0000000000002930                 mov     eax, dword ptr [rsp+550h+var_4E8]
.text:0000000000002934                 mov     [rsp+550h+biWidth_], r8d ; biWidth field saved locally

In preparation for copying file data, the parser allocates a buffer four times the provided biWidth value.

lfbmpx+1f85
.text:0000000000001F85                 mov     edx, r12d
.text:0000000000001F88                 mov     [rbp+37h+var_98], eax
.text:0000000000001F8B                 lea     eax, [rbx*4] ; biWidth
.text:0000000000001F92                 mov     r8d, 2E4h
.text:0000000000001F98                 movsxd  rcx, eax
.text:0000000000001F9B                 call    cs:L_LocalAlloc

To determine how many bytes to copy into this buffer, the biSize field from the BITMAPINFOHEADER is used.

lfbmpx+1ebe
.text:0000000000001EBE biSize_is_8:                               
.text:0000000000001EBE                 lea     eax, [rdx+3]     ; rdx -> biWidth
.text:0000000000001EC1                 mov     r8d, edx
.text:0000000000001EC4                 and     eax, 0FFFFFFFCh
.text:0000000000001EC7                 retn
.text:0000000000001EC8 biSize_is_16:                              
.text:0000000000001EC8                 lea     r8d, [rdx+rdx]   ; rdx -> biWidth
.text:0000000000001ECC                 lea     eax, [r8+3]
.text:0000000000001ED0                 and     eax, 0FFFFFFFCh
.text:0000000000001ED3                 retn
.text:0000000000001ED4 biSize_is_24:                              
.text:0000000000001ED4                 lea     r8d, [rdx+rdx*2]  ; rdx -> biWidth
.text:0000000000001ED8                 lea     eax, [r8+3]
.text:0000000000001EDC                 and     eax, 0FFFFFFFCh
.text:0000000000001EDF                 retn

The allocated buffer is then populated with memmove using the calculated value from the biSize switch statement above.

lfbmpx+20c5
.text:00000000000020C5                 mov     eax, r13d
.text:00000000000020C8                 mov     r12d, esi
.text:00000000000020CB                 movsxd  rdx, edi
.text:00000000000020CE                 sub     eax, edi
.text:00000000000020D0                 mov     rcx, r14        ; Dst
.text:00000000000020D3                 cmp     eax, esi
.text:00000000000020D5                 cmovb   r12d, eax
.text:00000000000020D9                 add     rdx, r15        ; Src
.text:00000000000020DC                 mov     r8d, r12d       ; Size
.text:00000000000020DF                 mov     ebx, r12d
.text:00000000000020E2                 call    memmove

It is possible for a BMP file to have a biWidth value that will be overflown when multiplied to allocate the buffer to hold image data. Because this buffer is too small, the memmove will cause an out of bounds write, resulting in a buffer overflow, potentially resulting in code execution.

Crash Information

rax=ddd8ddc5dddddddd rbx=000000000000139c rcx=000001d35c0b1010
rdx=000000000a411410 rsi=00000000c0000008 rdi=0000000000000000
rip=00007ffd858aa632 rsp=00000012396ef978 rbp=00000012396efa29
r8=000000000000139c  r9=000000000000009b r10=dddddddddddddddd
r11=000001d35c0b0ff0 r12=000000000000139c r13=000000000000139c
r14=000001d35c0b0ff0 r15=000001d3664c2400
iopl=0         nv up ei pl nz na pe nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
lfBmpX!LEAD_Load+0x4652:
00007ffd`858aa632 488941f0        mov     qword ptr [rcx-10h],rax ds:000001d3`5c0b1000=????????????????

> !analyze -v
FAILURE_EXCEPTION_CODE:  c0000005
FAILURE_IMAGE_NAME:  lfBmpX.DLL
BUCKET_ID_IMAGE_STR:  lfBmpX.DLL
FAILURE_MODULE_NAME:  lfBmpX
BUCKET_ID_MODULE_STR:  lfBmpX
FAILURE_FUNCTION_NAME:  LEAD_Load
BUCKET_ID_FUNCTION_STR:  LEAD_Load
BUCKET_ID_OFFSET:  4652
BUCKET_ID_MODTIMEDATESTAMP:  5c6efa90
BUCKET_ID_MODCHECKSUM:  1c9a0
BUCKET_ID_MODVER_STR:  20.0.0.1
BUCKET_ID_PREFIX_STR:  APPLICATION_FAULT_INVALID_POINTER_WRITE_AVRF_
FAILURE_PROBLEM_CLASS:  APPLICATION_FAULT
FAILURE_SYMBOL_NAME:  lfBmpX.DLL!LEAD_Load

Timeline

2019-08-11 - Vendor Disclosure
2019-11-03 - Vendor patched
2019-11-05 - Public Release

Credit

Discovered by Cory Duplantis of Cisco Talos.