CVE-2019-5076
An exploitable out-of-bounds write vulnerability exists in the igcore19d.dll PNG header-parser of the Accusoft ImageGear 19.3.0 library. A specially crafted PNG file can cause an out-of-bounds write, resulting in a remote code execution. An attacker needs to provide a malformed file to the viction 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
This vulnerability is present in the Accusoft ImageGear library, which is a document0imaging developer toolkit providing all kinds of functionality related with an image conversion, creation, editing, etc.
There is a vulnerability in the function responsible for handling the PNG header
. A specially crafted PNG IHDR Width field file can lead to an out of bounds write and remote code execution.
If we try to load a malformed PNG file via the IG_load_file
function, we end up in the following situation:
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
Time Travel Position: 488D3:0
igCore19d!IG_mpi_page_set+0x111d27:
00007fff`b8a00ec7 8022f0 and byte ptr [rdx],0F0h ds:00000247`f35d0001=??
0:000> kb
# RetAddr : Args to Child : Call Site
00 00007fff`b89fc097 : 00000247`f35c0000 000000b7`fa6fd0b0 00000000`00000005 00000000`00000005 : igCore19d!IG_mpi_page_set+0x111d27
01 00007fff`b89fd770 : 00000000`00000000 000000b7`fa6fee30 00000000`00000000 00000247`e3101f20 : igCore19d!IG_mpi_page_set+0x10cef7
02 00007fff`b89fa75d : 00000000`00000000 000000b7`fa6feee0 00000000`1000001a 00007fff`d28040a4 : igCore19d!IG_mpi_page_set+0x10e5d0
03 00007fff`b88bd704 : 00000247`dc910ff0 00000000`00000008 00000247`e30f7fe0 6cca9737`00000037 : igCore19d!IG_mpi_page_set+0x10b5bd
04 00007fff`b8906c99 : 00000000`00000021 000000b7`fa6ff908 000000b7`fa6ff3e0 00000000`00000000 : igCore19d!IG_image_savelist_get+0xef4
05 00007fff`b890653c : 00000247`dc844fc8 00007fff`b88fcc95 00000000`00000000 00000000`00000000 : igCore19d!IG_mpi_page_set+0x17af9
06 00007fff`b888e7c0 : 00000247`e2fc4fa5 00007fff`ece39570 00000000`00000000 00007fff`ece380b0 : igCore19d!IG_mpi_page_set+0x1739c
07 00007ff7`55ff1112 : 00007fff`ece39570 00000000`00000000 00007fff`ece39570 000000b7`fa6ff938 : igCore19d!IG_load_file+0x80
08 00007ff7`55ff122c : 00000247`e2fc4fa5 00000247`e2fc4fec 00000247`00000021 00000000`00001000 : igFuzzer!fuzzme+0x32 [d:\projects\imagegear\fuzzer\igfuzzer\igfuzzer\igfuzzer.cpp @ 47]
09 00007ff7`55ff1474 : 00000000`00000004 00000247`e2fc4f70 00007fff`efa83670 00000000`00000033 : igFuzzer!main+0xbc [d:\projects\imagegear\fuzzer\igfuzzer\igfuzzer\igfuzzer.cpp @ 79]
0a (Inline Function) : --------`-------- --------`-------- --------`-------- --------`-------- : igFuzzer!invoke_main+0x22 [d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78]
0b 00007fff`edc04034 : 00000000`00000000 00000000`00000000 00000000`00000ca8 00000000`00000000 : igFuzzer!__scrt_common_main_seh+0x10c [d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288]
0c 00007fff`efa83691 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14
0d 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21
As we can see, an out of bounds write has occurred. Looking at pseudo-code of this vulnerable function we can see that:
Line 1 __int64 __fastcall Set_Element(PBYTE buffer, unsigned __int8 a2, __int16 a3, __int16 a4)
Line 2 {
Line 3 int index; // er8
Line 4 BYTE *ptr; // rdx
Line 5 int v4;
Line 5.1 int v5;
Line 6 v4 = a2;
Line 7 v5 = a2 * a3;
Line 8 v6 = (v5 >> 31) & 7;
Line 9 result = ((v6 + v5) & 7u) - v6;
Line 10 index = (v6 + v5) >> 3;
Line 11
Line 12 (...)
Line 13
Line 14 ptr = &buffer[index];
Line 15 if ( (_DWORD)result )
Line 16 *ptr &= 0xF0u;
an access violation occurs at line 16
. Let us check out the buffer
variable:
0:000> !heap -p -a 0x000001a3e4859000
address 000001a3e4859000 found in
_DPH_HEAP_ROOT @ 1a3dcfd1000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
1a3e4bb7ea0: 1a3e4859000 ffff - 1a3e4858000 12000
00007ff85bf7f4bf ntdll!RtlDebugAllocateHeap+0x000000000000003f
00007ff85bf2b530 ntdll!RtlpAllocateHeap+0x000000000008f760
00007ff85be99725 ntdll!RtlpAllocateHeapInternal+0x00000000000005e5
00007ff82f2a6407 MSVCR110!malloc+0x000000000000005b
00007ff818e3781b igCore19d!AF_memm_alloc+0x000000000000002b
00007ff818f4bdfe igCore19d!IG_mpi_page_set+0x000000000010cc5e
00007ff818f4d770 igCore19d!IG_mpi_page_set+0x000000000010e5d0
00007ff818f4a75d igCore19d!IG_mpi_page_set+0x000000000010b5bd
00007ff818e0d704 igCore19d!IG_image_savelist_get+0x0000000000000ef4
00007ff818e56c99 igCore19d!IG_mpi_page_set+0x0000000000017af9
00007ff818e5653c igCore19d!IG_mpi_page_set+0x000000000001739c
00007ff818dde7c0 igCore19d!IG_load_file+0x0000000000000080
*** WARNING: Unable to verify checksum for igFuzzer.exe
00007ff633951112 igFuzzer!fuzzme+0x0000000000000032 [d:\projects\imagegear\fuzzer\igfuzzer\igfuzzer\igfuzzer.cpp @ 47]
00007ff63395122c igFuzzer!main+0x00000000000000bc [d:\projects\imagegear\fuzzer\igfuzzer\igfuzzer\igfuzzer.cpp @ 79]
00007ff633951474 igFuzzer!__scrt_common_main_seh+0x000000000000010c [d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288]
00007ff85b974034 KERNEL32!BaseThreadInitThunk+0x0000000000000014
00007ff85bef3691 ntdll!RtlUserThreadStart+0x0000000000000021
The ptr
variable points to a location 000001a3
e4855001 which is a location below the beginning of the
buffer` variable allocated space.
That situation appears because calculated index
has the value : 0xffffffffffffc001
which added to beginning of the buffer
address causes an integer overflow.
The size of the buffer
, based on further analysis, is strictly related with the IHDR -> Width
chunk field value. Which, in our case is equal to 0xFFFF
.
Let us step out of this Set_Element
function and see from where the two arguments based on which the index
value is calculated are comming from:
Line 1 internalIndex = *((unsigned __int8 *)&v104 + v34 + 1);
Line 2 counter = 0;
Line 3 if ( *ptrToTableOfValues > 0u )
Line 4 {
Line 5 eleSize = elements[eleIndex + 1];
Line 6 do
Line 7 {
Line 8 if ( (signed __int64)DIB_bit_depth_get(objContext) > 16 )
Line 9 {
Line 10 v45 = 0i64;
Line 11 if ( (signed int)v38 > 0 )
Line 12 {
Line 13 v46 = v38 * counter;
Line 14 do
Line 15 {
Line 16 v47 = *((_BYTE *)v19 + v46);
Line 17 v46 = (unsigned int)(v46 + 1);
Line 18 v48 = v45++ + *(_QWORD *)&array[8 * index];
Line 19 *(_BYTE *)(v48 + internalIndex * (unsigned __int64)v38) = v47;
Line 20 }
Line 21 while ( v45 < v38 );
Line 22 }
Line 23 }
Line 24 else
Line 25 {
Line 26 v41 = (unsigned __int64)DIB_bit_depth_get(objContext);
Line 27 v42 = (signed int)(v41 * counter) / 8;
Line 28 if ( v41 > 8u )
Line 29 v43 = v19[v42 / 2];
Line 30 else
Line 31 v43 = ((unsigned __int16)((*((unsigned __int8 *)v19 + v42) << (signed int)(v41 * counter) % -8) & 0xFF) >> (8 - v41)) & 0xFF;
Line 32 bitDepth = (unsigned __int64)DIB_bit_depth_get(objContext);
Line 33 Set_Element(*(PBYTE *)&array[8 * index], bitDepth, internalIndex, v43);
Line 34 }
Line 35 ++counter;
Line 36 internalIndex += eleSize;
Line 37 }
Line 38 while ( counter < *maxValue );
We can see that while loop
is controlled by the maxValue
variable which in our case is equal 0x7FFF
(it is IHDR->Width / 2
)
Each loop cycle the internalIndex
variable is incremented by eleSize
(in our case 2). Its easy to guess that internalIndex
variable will be bigger than 0x7FFF
because the number of iterations depends on maxValue
and counter
, which is incremented during each cycle just by 1.
A value bigger than 0x7FFF
(INT_MAX) for the internalIndex
variable has additional consequences inside the Set_Elements
function.
The internalIndex
value is passed to the Set_Elements
function as the third argument in the form of __int16
(signed 16-bit integer). It means that its value
in our case 0x8001
is smaller than 0 and thats why during the multiplication we end up with index
< 0 ( 0xffffffffffffc001
) what at the end results in ptr
pointing to an address below
the beginning of the buffer
space.
Additionaly there is no check to see whether the calculated index
is not bigger than the buffer
size.
There are some checks inside PNG_get_deflate_data
function related to IHDR->Width
but for Width
in the range of <0xFFBA; 0xFFFF> they are bypased and OOBW takes place.
Also to trigger this vulnerability DIB_bit_depth
needs to be smaller or equal 16.
All these circumstances lead to out of bounds write and in consequences can allow an attacker to achieve remote code execution.
0:000> !analyze -v
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************
DUMP_CLASS: 2
DUMP_QUALIFIER: 2
MODLIST_WITH_TSCHKSUM_HASH: 1aa67d2c18112c8decc3d1fdc606b26bc45cbfe0
MODLIST_SHA1_HASH: 32defb2ff0bde8e528d48523aab41a049e5a1db6
NTGLOBALFLAG: 2100000
APPLICATION_VERIFIER_FLAGS: 0
PRODUCT_TYPE: 1
SUITE_MASK: 272
DUMP_TYPE: fe
APPLICATION_VERIFIER_LOADED: 1
FAULTING_IP:
igCore19d!IG_mpi_page_set+111d27
00007fff`b8a00ec7 8022f0 and byte ptr [rdx],0F0h
EXCEPTION_RECORD: (.exr -1)
ExceptionAddress: 00007fffb8a00ec7 (igCore19d!IG_mpi_page_set+0x0000000000111d27)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 0000000000000000
Parameter[1]: 00000247f35d0001
Attempt to read from address 00000247f35d0001
FAULTING_THREAD: 000032a0
PROCESS_NAME: igFuzzer.exe
FOLLOWUP_IP:
igCore19d!IG_mpi_page_set+111d27
00007fff`b8a00ec7 8022f0 and byte ptr [rdx],0F0h
READ_ADDRESS: 00000247f35d0001
ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.
EXCEPTION_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: 0000000000000000
EXCEPTION_PARAMETER2: 00000247f35d0001
WATSON_BKT_PROCSTAMP: 5d26f980
WATSON_BKT_MODULE: igCore19d.dll
WATSON_BKT_MODSTAMP: 5c101edf
WATSON_BKT_MODOFFSET: 190ec7
WATSON_BKT_MODVER: 19.3.0.0
MODULE_VER_PRODUCT: Accusoft ImageGear
BUILD_VERSION_STRING: 10.0.17134.753 (WinBuild.160101.0800)
ANALYSIS_SESSION_HOST: ICELENOVO
ANALYSIS_SESSION_TIME: 07-18-2019 08:57:13.0937
ANALYSIS_VERSION: 10.0.18914.1001 amd64fre
THREAD_ATTRIBUTES:
OS_LOCALE: ENU
BUGCHECK_STR: APPLICATION_FAULT_INVALID_POINTER_READ_AVRF
DEFAULT_BUCKET_ID: INVALID_POINTER_READ_AVRF
PRIMARY_PROBLEM_CLASS: APPLICATION_FAULT
PROBLEM_CLASSES:
ID: [0n313]
Type: [@ACCESS_VIOLATION]
Class: Addendum
Scope: BUCKET_ID
Name: Omit
Data: Omit
PID: [Unspecified]
TID: [0x32a0]
Frame: [0] : igCore19d!IG_mpi_page_set
ID: [0n285]
Type: [INVALID_POINTER_READ]
Class: Primary
Scope: DEFAULT_BUCKET_ID (Failure Bucket ID prefix)
BUCKET_ID
Name: Add
Data: Omit
PID: [Unspecified]
TID: [0x32a0]
Frame: [0] : igCore19d!IG_mpi_page_set
ID: [0n98]
Type: [AVRF]
Class: Addendum
Scope: DEFAULT_BUCKET_ID (Failure Bucket ID prefix)
BUCKET_ID
Name: Add
Data: Omit
PID: [0x2d80]
TID: [0x32a0]
Frame: [0] : igCore19d!IG_mpi_page_set
LAST_CONTROL_TRANSFER: from 00007fffb89fc097 to 00007fffb8a00ec7
STACK_TEXT:
000000b7`fa6fcfa8 00007fff`b89fc097 : 00000247`f35c0000 000000b7`fa6fd0b0 00000000`00000005 00000000`00000005 : igCore19d!IG_mpi_page_set+0x111d27
000000b7`fa6fcfb0 00007fff`b89fd770 : 00000000`00000000 000000b7`fa6fee30 00000000`00000000 00000247`e3101f20 : igCore19d!IG_mpi_page_set+0x10cef7
000000b7`fa6fed80 00007fff`b89fa75d : 00000000`00000000 000000b7`fa6feee0 00000000`1000001a 00007fff`d28040a4 : igCore19d!IG_mpi_page_set+0x10e5d0
000000b7`fa6fede0 00007fff`b88bd704 : 00000247`dc910ff0 00000000`00000008 00000247`e30f7fe0 6cca9737`00000037 : igCore19d!IG_mpi_page_set+0x10b5bd
000000b7`fa6ff2f0 00007fff`b8906c99 : 00000000`00000021 000000b7`fa6ff908 000000b7`fa6ff3e0 00000000`00000000 : igCore19d!IG_image_savelist_get+0xef4
000000b7`fa6ff370 00007fff`b890653c : 00000247`dc844fc8 00007fff`b88fcc95 00000000`00000000 00000000`00000000 : igCore19d!IG_mpi_page_set+0x17af9
000000b7`fa6ff850 00007fff`b888e7c0 : 00000247`e2fc4fa5 00007fff`ece39570 00000000`00000000 00007fff`ece380b0 : igCore19d!IG_mpi_page_set+0x1739c
000000b7`fa6ff890 00007ff7`55ff1112 : 00007fff`ece39570 00000000`00000000 00007fff`ece39570 000000b7`fa6ff938 : igCore19d!IG_load_file+0x80
000000b7`fa6ff8e0 00007ff7`55ff122c : 00000247`e2fc4fa5 00000247`e2fc4fec 00000247`00000021 00000000`00001000 : igFuzzer!fuzzme+0x32
000000b7`fa6ff930 00007ff7`55ff1474 : 00000000`00000004 00000247`e2fc4f70 00007fff`efa83670 00000000`00000033 : igFuzzer!main+0xbc
000000b7`fa6ff970 00007fff`edc04034 : 00000000`00000000 00000000`00000000 00000000`00000ca8 00000000`00000000 : igFuzzer!__scrt_common_main_seh+0x10c
000000b7`fa6ff9b0 00007fff`efa83691 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14
000000b7`fa6ff9e0 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21
STACK_COMMAND: ~0s ; .cxr ; kb
THREAD_SHA1_HASH_MOD_FUNC: 5fc40328a3686d13dfccd6be23cd62ee5b669c24
THREAD_SHA1_HASH_MOD_FUNC_OFFSET: e577b2c9c4565bf4611a915c81bb2af79ea54a08
THREAD_SHA1_HASH_MOD: 95fb16a8183863913ce23932c842443f579c4d87
FAULT_INSTR_CODE: b9f02280
SYMBOL_STACK_INDEX: 0
SYMBOL_NAME: igCore19d!IG_mpi_page_set+111d27
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: igCore19d
IMAGE_NAME: igCore19d.dll
DEBUG_FLR_IMAGE_TIMESTAMP: 5c101edf
FAILURE_BUCKET_ID: INVALID_POINTER_READ_AVRF_c0000005_igCore19d.dll!IG_mpi_page_set
BUCKET_ID: APPLICATION_FAULT_INVALID_POINTER_READ_AVRF_igCore19d!IG_mpi_page_set+111d27
FAILURE_EXCEPTION_CODE: c0000005
FAILURE_IMAGE_NAME: igCore19d.dll
BUCKET_ID_IMAGE_STR: igCore19d.dll
FAILURE_MODULE_NAME: igCore19d
BUCKET_ID_MODULE_STR: igCore19d
FAILURE_FUNCTION_NAME: IG_mpi_page_set
BUCKET_ID_FUNCTION_STR: IG_mpi_page_set
BUCKET_ID_OFFSET: 111d27
BUCKET_ID_MODTIMEDATESTAMP: 5c101edf
BUCKET_ID_MODCHECKSUM: 41928b
BUCKET_ID_MODVER_STR: 19.3.0.0
BUCKET_ID_PREFIX_STR: APPLICATION_FAULT_INVALID_POINTER_READ_AVRF_
FAILURE_PROBLEM_CLASS: APPLICATION_FAULT
FAILURE_SYMBOL_NAME: igCore19d.dll!IG_mpi_page_set
TARGET_TIME: 2019-07-16T14:59:31.000Z
OSBUILD: 9200
OSSERVICEPACK: 753
SERVICEPACK_NUMBER: 0
OS_REVISION: 0
OSPLATFORM_TYPE: x64
OSNAME: Windows 8
OSEDITION: Windows 8
USER_LCID: 0
OSBUILD_TIMESTAMP: unknown_date
BUILDDATESTAMP_STR: 160101.0800
BUILDLAB_STR: WinBuild
BUILDOSVER_STR: 10.0.17134.753
ANALYSIS_SESSION_ELAPSED_TIME: 22489
ANALYSIS_SOURCE: UM
FAILURE_ID_HASH_STRING: um:invalid_pointer_read_avrf_c0000005_igcore19d.dll!ig_mpi_page_set
FAILURE_ID_HASH: {bfd6b5ab-5824-8327-06e6-1c2f38a120f0}
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-07-30 - Vendor Disclosure
2019-11-27 - Vendor patched
2019-12-02 - Public Release
Discovered by Marcin 'Icewall' Noga of Cisco Talos.