CVE-2019-5133
An exploitable out-of-bounds write vulnerability exists in the igcore19d.dll BMP parser of the ImageGear 19.3.0 library. 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.3.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-787: Out-of-bounds Write
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 BMP image parser. 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:
0:000> g
(4630.28c4): Access violation - code c0000005 (first/second chance not available)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
Time Travel Position: 193FF9:0
eax=000000d0 ebx=00000008 ecx=220e7ff8 edx=00000001 esi=00000008 edi=220ebff8
eip=63c8472f esp=004fee94 ebp=004feec0 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
igCore19d!IG_mpi_page_set+0x8bcf:
63c8472f 88040e mov byte ptr [esi+ecx],al ds:002b:220e8000=??
Pseudo-code of the vulnerable function looks like this:
Line 1 void __cdecl sub_63C84560(int a1, int arg_biCompression, int a3, int a4, int biWidth, int src_buffer, PBYTE dstBuffer, int calculatedSize)
Line 2 {
Line 3 type = a4;
Line 4 biCompression = arg_biCompression;
Line 5 if ( calculatedSize < biWidth * arg_biCompression * (a4 >> 3) )
Line 6 sub_63C11590(
Line 7 -401,
Line 8 "Insufficient Dst buffer size.",
Line 9 calculatedSize,
Line 10 biWidth * arg_biCompression * (a4 >> 3),
Line 11 "..\\..\\..\\..\\Common\\Core\\Raster.cpp",
Line 12 857);
Line 13 (...)
Line 14 out_index = 0;
Line 15 index = 0;
Line 16 v26 = 0;
Line 17 if ( biWidth > 0 )
Line 18 {
Line 19 biBitCount = a1;
Line 20 do
Line 21 {
Line 22 if ( biBitCount == 8 )
Line 23 {
Line 24 srcElement = *(unsigned __int8 *)(out_index + src_buffer);
Line 25 }
Line 26 else if ( biBitCount == 16 )
Line 27 {
Line 28 srcElement = *(unsigned __int16 *)(src_buffer + 2 * out_index);
Line 29 }
Line 30 else
Line 31 {
Line 32 if ( biBitCount != 32 )
Line 33 sub_63C11590(-401, 0, biBitCount, 0, "..\\..\\..\\..\\Common\\Core\\Raster.cpp", 881);
Line 34 srcElement = *(_DWORD *)(src_buffer + 4 * out_index);
Line 35 }
Line 36 v25 = srcElement;
Line 37 if ( biCompression > 0 )
Line 38 {
Line 39 v20 = v24;
Line 40 tmp_biCompression = arg_biCompression;
Line 41 v22 = a3 - (_DWORD)v24;
Line 42 do
Line 43 {
Line 44 element = (srcElement & *(_DWORD *)((char *)v20 + v22)) >> *v20;
Line 45 if ( type == 8 )
Line 46 {
Line 47 dstBuffer[index] = element;
Line 48 }
Line 49 else if ( type == 16 )
Line 50 {
Line 51 *(_WORD *)&dstBuffer[2 * index] = element;
Line 52 }
Line 53 else
Line 54 {
Line 55 if ( type != 32 )
Line 56 sub_63C11590(-401, 0, type, 0, "..\\..\\..\\..\\Common\\Core\\Raster.cpp", 906);
Line 57 *(_DWORD *)&dstBuffer[4 * index] = element;
Line 58 }
Line 59 v22 = a3 - (_DWORD)v24;
Line 60 srcElement = v25;
Line 61 ++index;
Line 62 ++v20;
Line 63 --tmp_biCompression;
Line 64 }
Line 65 while ( tmp_biCompression );
Line 66 out_index = v26;
Line 67 biCompression = arg_biCompression;
Line 68 }
Line 69 biBitCount = a1;
Line 70 v26 = ++out_index;
Line 71 }
Line 72 while ( out_index < biWidth );
Line 73 }
The out-of-bounds write appears at line 47. We can see that there are two while loops where the internal one is controlled by the biCompression
value (a fixed value passed as argument) equal to three and the outer while is controlled by
biWidth
value, which is read from the file, which in our case has the value 0x40000000.
The space fordstBuffer
is allocated based on the following formula formula:
unsigned int __thiscall sub_63C494D0(struct_this *this)
{
return ((this->biWidth * this->biCompression * this->dword38 + 31) >> 3) & 0xFFFFFFFC;
}
this->dword38 = 0x20 - const
this->biCompression = 3
this->biWidth = 0x40000000
In our case, the result from above formula is equal to 0
so the allocated space for a dstBuffer
is 8 bytes in size in our case (Windows 10/x86). We can also notice that the check done at line 5
is bypassed because the signed values are compared and the right side of the inequality is below zero (0xc0000000).
An attacker could control all presented variables through proper file content manipulation. Using the biWidth
field attackers can cause an out-of-bounds write, leading to memory corruption which in turn can allow them to achieve remote code execution.
0:000> !analyze -v
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************
*** WARNING: Unable to verify checksum for FuzzmeHeap.exe
KEY_VALUES_STRING: 1
Key : AV.Fault
Value: Write
Key : Analysis.CPU.Sec
Value: 6
Key : Analysis.DebugAnalysisProvider.CPP
Value: Create: 8007007e on ICELENOVO
Key : Analysis.DebugData
Value: CreateObject
Key : Analysis.DebugModel
Value: CreateObject
Key : Analysis.Elapsed.Sec
Value: 7
Key : Analysis.Memory.CommitPeak.Mb
Value: 445
Key : Analysis.System
Value: CreateObject
Key : Timeline.OS.Boot.DeltaSec
Value: 2237994
APPLICATION_VERIFIER_LOADED: 1
EXCEPTION_RECORD: (.exr -1)
ExceptionAddress: 63c8472f (igCore19d!IG_mpi_page_set+0x00008bcf)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000001
Parameter[1]: 220e8000
Attempt to write to address 220e8000
FAULTING_THREAD: 000028c4
PROCESS_NAME: FuzzmeHeap.exe
WRITE_ADDRESS: 220e8000
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: 220e8000
STACK_TEXT:
WARNING: Stack unwind information not available. Following frames may be wrong.
004feec0 63c32ca0 00000010 00000003 004fef6c igCore19d!IG_mpi_page_set+0x8bcf
004feee8 63cee2cf 00000010 00000003 004fef6c igCore19d!IG_thread_image_unlock+0x42c0
004fef7c 63cf05e3 004ff51c 1000001b 18d0efe8 igCore19d!IG_mpi_page_set+0x7276f
004fefcc 63ced685 004ff51c 1000001b 18d0efe8 igCore19d!IG_mpi_page_set+0x74a83
004ff494 63c50ff9 004ff51c 18d0efe8 00000001 igCore19d!IG_mpi_page_set+0x71b25
004ff4cc 63c90437 00000000 18d0efe8 004ff51c igCore19d!IG_image_savelist_get+0xb29
004ff748 63c8fd99 00000000 18cb6fc0 00000001 igCore19d!IG_mpi_page_set+0x148d7
004ff768 63c26767 00000000 18cb6fc0 00000001 igCore19d!IG_mpi_page_set+0x14239
004ff788 007110af 18cb6fc0 004ff7a0 19880fb4 igCore19d!IG_load_file+0x47
004ff7ac 00711252 18cb6fc0 004ff7e4 00000021 FuzzmeHeap!fuzzme+0x2f
004ff80c 00711d55 00000003 19880f70 15f3bf50 FuzzmeHeap!fuzzme+0x1d2
004ff854 756a6359 00368000 756a6340 004ff8c0 FuzzmeHeap!fuzzme+0xcd5
004ff864 77dc7b74 00368000 5cc4ad35 00000000 KERNEL32!BaseThreadInitThunk+0x19
004ff8c0 77dc7b44 ffffffff 77de8f2a 00000000 ntdll!__RtlUserThreadStart+0x2f
004ff8d0 00000000 00711dcb 00368000 00000000 ntdll!_RtlUserThreadStart+0x1b
STACK_COMMAND: ~0s ; .cxr ; kb
SYMBOL_NAME: igCore19d!IG_mpi_page_set+8bcf
MODULE_NAME: igCore19d
IMAGE_NAME: igCore19d.dll
FAILURE_BUCKET_ID: INVALID_POINTER_WRITE_AVRF_c0000005_igCore19d.dll!IG_mpi_page_set
OSPLATFORM_TYPE: x86
OSNAME: Windows 8
FAILURE_ID_HASH: {39ff52ad-9054-81fd-3e4d-ef5d82e4b2c1}
Followup: MachineOwner
---------
0:000> lmv a rip
Browse full module list
start end module name
00007fff`b8870000 00007fff`b8c83000 igCore19d (export symbols) igCore19d.dll
Loaded symbol image file: igCore19d.dll
Mapped memory image file: d:\projects\ImageGear\Build\Bin\x64\igCore19d.dll
Image path: d:\projects\ImageGear\Build\Bin\x64\igCore19d.dll
Image name: igCore19d.dll
Browse all global symbols functions data
Timestamp: Tue Dec 11 16:32:31 2018 (5C101EDF)
CheckSum: 0041928B
ImageSize: 00413000
File version: 19.3.0.0
Product version: 19.3.0.0
File flags: 0 (Mask 3F)
File OS: 4 Unknown Win32
File type: 2.0 Dll
File date: 00000000.00000000
Translations: 0409.04b0
Information from resource tables:
CompanyName: Accusoft Corporation
ProductName: Accusoft ImageGear
InternalName: igcore19d.dll
OriginalFilename: igcore19d.dll
ProductVersion: 19.3.0.0
FileVersion: 19.3.0.0
FileDescription: Accusoft ImageGear CORE DLL
LegalCopyright: Copyright© 1996-2018 Accusoft Corporation. All rights reserved.
LegalTrademarks: ImageGearÆ and AccusoftÆ are registered trademarks of Accusoft Corporation
2019-10-23 - Vendor Disclosure
2019-11-27 - Vendor Patched
2019-12-02 - Public Release
Discovered by Marcin 'Icewall' Noga of Cisco Talos.