CVE-2020-6065
An exploitable out-of-bounds write vulnerability exists in the bmp_parsing
function of the igcore19d.dll library of Accusoft ImageGear, version 19.5.0. A specially crafted BMP file can cause an out-of-bounds write, resulting in a remote code execution. An attacker needs to provide a malformed file to the victim to trigger the vulnerability.
Accusoft ImageGear 19.5.0
https://www.accusoft.com/products/imagegear/overview/
9.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CWE-190: Integer Overflow or Wraparound
The ImageGear library is a document imaging developer toolkit providing all kinds of functionality related to image conversion, creation, editing, annotation, etc. It supports more than 100 formats, including many image formats, DICOM, PDF, Microsoft Office and others.
There is a vulnerability in the uncompress_scan_line
function, due to an integer overflow. A specially crafted BMP file can lead to an out-of-bounds write, which can result in remote code execution.
Trying to load a malformed BMP file via IG_load_file
function we end up in the following situation:
eax=00000040 ebx=00000002 ecx=0e0d4f90 edx=00000000 esi=0e832ffe edi=05996ff1
eip=568edbb3 esp=006fee24 ebp=006fee7c iopl=0 nv up ei ng nz na po cy
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010283
igCore19d!IG_mpi_page_set+0x72823:
568edbb3 884602 mov byte ptr [esi+2],al ds:002b:0e833000=??
0:000> kb
# ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
00 006fee7c 568efdd9 006ff40c 1000001b 0e05afe8 igCore19d!IG_mpi_page_set+0x72823
01 006feebc 568ece25 006ff40c 1000001b 0e05afe8 igCore19d!IG_mpi_page_set+0x74a49
02 006ff384 568507c9 006ff40c 0e05afe8 00000001 igCore19d!IG_mpi_page_set+0x71a95
03 006ff3bc 5688fb97 00000000 0e05afe8 006ff40c igCore19d!IG_image_savelist_get+0xb29
04 006ff638 5688f4f9 00000000 0984bfa8 00000001 igCore19d!IG_mpi_page_set+0x14807
05 006ff658 56826007 00000000 0984bfa8 00000001 igCore19d!IG_mpi_page_set+0x14169
06 006ff678 00d859ac 0984bfa8 006ff764 006ff788 igCore19d!IG_load_file+0x47
07 006ff778 00d861a7 0984bfa8 006ff8ac 00000021 simple_exe_141+0x159ac
08 006ff944 00d86cbe 00000005 097f8f50 096e1f40 simple_exe_141+0x161a7
09 006ff958 00d86b27 d4d1a41c 00d815e1 00d815e1 simple_exe_141+0x16cbe
0a 006ff9b4 00d869bd 006ff9c4 00d86d38 006ff9d4 simple_exe_141+0x16b27
0b 006ff9bc 00d86d38 006ff9d4 76cd6359 0052f000 simple_exe_141+0x169bd
0c 006ff9c4 76cd6359 0052f000 76cd6340 006ffa30 simple_exe_141+0x16d38
0d 006ff9d4 77577b74 0052f000 a1cf5680 00000000 KERNEL32!BaseThreadInitThunk+0x19
0e 006ffa30 77577b44 ffffffff 77598ef2 00000000 ntdll!__RtlUserThreadStart+0x2f
0f 006ffa40 00000000 00d815e1 0052f000 00000000 ntdll!_RtlUserThreadStart+0x1b
As we can see, an out-of-bounds operation occurred.
The pseudo-code of this vulnerable function looks like this:
LINE1 int __stdcall bmp_parsing(int arg0, void *a1, _DWORD *a3, bitmap_object *bmp_object)
LINE2 {
LINE3 bitmap_object *_bmp_object; // edi
LINE4 int v5; // esi
LINE5 int width; // eax
LINE6 int total_width; // ebx
LINE7 int v8; // esi
LINE8 int result; // eax
LINE9 _BYTE *buffer_allocated; // esi
LINE10 int v11; // ebx
LINE11 _BYTE *v12; // edi
LINE12 int v13; // eax
LINE13 int v14; // eax
LINE14 unsigned int v15; // eax
LINE15 unsigned int v16; // [esp-4h] [ebp-58h]
LINE16 int v17[13]; // [esp+Ch] [ebp-48h]
LINE17 int v18; // [esp+40h] [ebp-14h]
LINE18 int v19; // [esp+44h] [ebp-10h]
LINE19 int v20; // [esp+48h] [ebp-Ch]
LINE20 void *_buffer_allocated; // [esp+4Ch] [ebp-8h]
LINE21 unsigned int v22; // [esp+50h] [ebp-4h]
LINE22
LINE23 _bmp_object = bmp_object;
LINE24 v5 = 0;
LINE25 width = getBiWidth(bmp_object);
LINE26 total_width = 4 * width; [3]
LINE27 v19 = 4 * width;
LINE28 sub_5681AF60(arg0, (int)a1, (int)v17, 20 * width, 1);
LINE29 _buffer_allocated = AF_memm_alloc((int)a1, total_width, (int)"..\\..\\..\\..\\Common\\Formats\\bmpread.c", 1818); [2]
LINE30 if ( _buffer_allocated )
LINE31 {
LINE32 v22 = 0;
LINE33 if ( getSizeY_0((IGDIBOject *)bmp_object) )
LINE34 {
LINE35 v20 = -1;
LINE36 do
LINE37 {
LINE38 v18 = sub_5681B280(v17, total_width);
LINE39 if ( !v18 )
LINE40 break;
LINE41 buffer_allocated = _buffer_allocated;
LINE42 v11 = 0;
LINE43 if ( getBiWidth(_bmp_object) > 0 )
LINE44 {
LINE45 v12 = (_BYTE *)(v18 + 1);
LINE46 do
LINE47 {
LINE48 *buffer_allocated = v12[1];
LINE49 buffer_allocated[1] = *v12;
LINE50 buffer_allocated[2] = *(v12 - 1); [1]
LINE51 buffer_allocated += 3;
LINE52 if ( sub_567D92B0(bmp_object) == 501 )
LINE53 *buffer_allocated++ = v12[2];
LINE54 ++v11;
LINE55 v12 += 4;
LINE56 }
LINE57 while ( v11 < getBiWidth(bmp_object) );
LINE58 _bmp_object = bmp_object;
LINE59 }
...
In this code we can observe a function bmp_parsing
, whose objective is to decompress the bmp data, is crashing while filling the buffer buffer_allocated
in [1].
At [3], the total_width
is calculated by multiplying the width
by 4. However, there is no integer overflow check on this operation, which could lead to a wraparound. This would further lead to the allocation of the buffer at [2] using an improper size, making the loop at [1] to write out of bounds.
0:000> !analyze -v
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************
KEY_VALUES_STRING: 1
Key : AV.Fault
Value: Write
Key : Analysis.CPU.Sec
Value: 1
Key : Analysis.DebugAnalysisProvider.CPP
Value: Create: 8007007e on DESKTOP-PJK7PVH
Key : Analysis.DebugData
Value: CreateObject
Key : Analysis.DebugModel
Value: CreateObject
Key : Analysis.Elapsed.Sec
Value: 9
Key : Analysis.Memory.CommitPeak.Mb
Value: 73
Key : Analysis.System
Value: CreateObject
Key : Timeline.OS.Boot.DeltaSec
Value: 320918
Key : Timeline.Process.Start.DeltaSec
Value: 1022
ADDITIONAL_XML: 1
APPLICATION_VERIFIER_LOADED: 1
EXCEPTION_RECORD: (.exr -1)
ExceptionAddress: 568edbb3 (igCore19d!IG_mpi_page_set+0x00072823)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000001
Parameter[1]: 0e833000
Attempt to write to address 0e833000
FAULTING_THREAD: 00001c80
PROCESS_NAME: simple.exe_141.exe
WRITE_ADDRESS: 0e833000
ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.
EXCEPTION_CODE_STR: c0000005
EXCEPTION_PARAMETER1: 00000001
EXCEPTION_PARAMETER2: 0e833000
STACK_TEXT:
WARNING: Stack unwind information not available. Following frames may be wrong.
006fee7c 568efdd9 006ff40c 1000001b 0e05afe8 igCore19d!IG_mpi_page_set+0x72823
006feebc 568ece25 006ff40c 1000001b 0e05afe8 igCore19d!IG_mpi_page_set+0x74a49
006ff384 568507c9 006ff40c 0e05afe8 00000001 igCore19d!IG_mpi_page_set+0x71a95
006ff3bc 5688fb97 00000000 0e05afe8 006ff40c igCore19d!IG_image_savelist_get+0xb29
006ff638 5688f4f9 00000000 0984bfa8 00000001 igCore19d!IG_mpi_page_set+0x14807
006ff658 56826007 00000000 0984bfa8 00000001 igCore19d!IG_mpi_page_set+0x14169
006ff678 00d859ac 0984bfa8 006ff764 006ff788 igCore19d!IG_load_file+0x47
006ff778 00d861a7 0984bfa8 006ff8ac 00000021 simple_exe_141+0x159ac
006ff944 00d86cbe 00000005 097f8f50 096e1f40 simple_exe_141+0x161a7
006ff958 00d86b27 d4d1a41c 00d815e1 00d815e1 simple_exe_141+0x16cbe
006ff9b4 00d869bd 006ff9c4 00d86d38 006ff9d4 simple_exe_141+0x16b27
006ff9bc 00d86d38 006ff9d4 76cd6359 0052f000 simple_exe_141+0x169bd
006ff9c4 76cd6359 0052f000 76cd6340 006ffa30 simple_exe_141+0x16d38
006ff9d4 77577b74 0052f000 a1cf5680 00000000 KERNEL32!BaseThreadInitThunk+0x19
006ffa30 77577b44 ffffffff 77598ef2 00000000 ntdll!__RtlUserThreadStart+0x2f
006ffa40 00000000 00d815e1 0052f000 00000000 ntdll!_RtlUserThreadStart+0x1b
STACK_COMMAND: ~0s ; .cxr ; kb
SYMBOL_NAME: igCore19d!IG_mpi_page_set+72823
MODULE_NAME: igCore19d
IMAGE_NAME: igCore19d.dll
FAILURE_BUCKET_ID: INVALID_POINTER_WRITE_AVRF_c0000005_igCore19d.dll!IG_mpi_page_set
OS_VERSION: 10.0.18362.239
BUILDLAB_STR: 19h1_release_svc_prod1
OSPLATFORM_TYPE: x86
OSNAME: Windows 10
FAILURE_ID_HASH: {39ff52ad-9054-81fd-3e4d-ef5d82e4b2c1}
Followup: MachineOwner
---------
2020-01-27 - Vendor Disclosure
2020-02-10 - Public Release
Discovered by Emmanuel Tacheau of Cisco Talos.