Headline
CVE-2019-5083: TALOS-2019-0875 || Cisco Talos Intelligence Group
An exploitable out-of-bounds write vulnerability exists in the igcore19d.dll TIFdecodethunderscan function of Accusoft ImageGear 19.3.0 library. A specially crafted TIFF 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.
Summary
An exploitable out-of-bounds write vulnerability exists in the igcore19d.dll TIF_decode_thunderscan function of Accusoft ImageGear 19.3.0 library. A specially crafted TIFF 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.
Tested Versions
Accusoft ImageGear 19.3.0
Product URLs
https://www.accusoft.com/products/imagegear/overview/
CVSSv3 Score
9.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CWE
CWE-787: Out-of-bounds Write
Details
This vulnerability is present in the Accusoft ImageGear library which is a document imaging developer toolkit providing all kinds of functionality related with an image conversion, creation, editing,etc.
There is a vulnerability in the TIF_decode_thunderscan function . A specially crafted TIFF file can lead to an out of bounds write and remote code execution. Trying to load a malformed TIFF file via IG_load_file function, we end up in the following situation:
(21b8.2a30): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
igCore19d!IG_mpi_page_set+0x1402e0:
00007ff8`1d35f480 f3aa rep stos byte ptr [rdi]
0:000> kb
# RetAddr : Args to Child : Call Site
00 00007ff8`1d35e760 : 000000de`ce1ef210 00007ff8`1d35f23f 000001e4`08fb8ff0 00000000`00000000 : igCore19d!IG_mpi_page_set+0x1402e0
01 00007ff8`1d35df56 : 000000de`ce1ef8b0 00000000`1000001e 00000000`00000003 000001e4`08b65d50 : igCore19d!IG_mpi_page_set+0x13f5c0
02 00007ff8`1d362867 : 00000000`00000004 000000de`ce1ef8b0 00000000`00000000 000001e4`000003e8 : igCore19d!IG_mpi_page_set+0x13edb6
03 00007ff8`1d35b154 : 00000000`00000001 000001e4`08b65d50 00000000`1000001e 000000de`ce1ef380 : igCore19d!IG_mpi_page_set+0x1436c7
04 00007ff8`1d1ed704 : 000001e4`022e8ff0 00000000`00000004 00000000`00000000 00007ff8`00000037 : igCore19d!IG_mpi_page_set+0x13bfb4
05 00007ff8`1d236c99 : 00000000`00000028 000000de`ce1efdd8 000000de`ce1ef8b0 00000000`00000000 : igCore19d!IG_image_savelist_get+0xef4
06 00007ff8`1d23653c : 000001e4`0221cfc8 00007ff8`1d22cc95 00000000`00000000 00000000`00000000 : igCore19d!IG_mpi_page_set+0x17af9
07 00007ff8`1d1be7c0 : 000001e4`08908fa5 00007ff8`58519570 00000000`00000000 00007ff8`585180b0 : igCore19d!IG_mpi_page_set+0x1739c
*** WARNING: Unable to verify checksum for igFuzzer.exe
08 00007ff6`33951112 : 00007ff8`58519570 00000000`00000000 00007ff8`58519570 000000de`ce1efe08 : igCore19d!IG_load_file+0x80
09 00007ff6`3395122c : 000001e4`08908fa5 000001e4`08908fe8 000001e4`00000021 00000000`00001000 : igFuzzer!fuzzme+0x32 [d:\projects\imagegear\fuzzer\igfuzzer\igfuzzer\igfuzzer.cpp @ 47]
0a 00007ff6`33951474 : 00000000`00000004 000001e4`08908f70 00000000`00000000 00000000`00000000 : igFuzzer!main+0xbc [d:\projects\imagegear\fuzzer\igfuzzer\igfuzzer\igfuzzer.cpp @ 79]
0b (Inline Function) : --------`-------- --------`-------- --------`-------- --------`-------- : igFuzzer!invoke_main+0x22 [d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78]
0c 00007ff8`5b974034 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : igFuzzer!__scrt_common_main_seh+0x10c [d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288]
0d 00007ff8`5bef3691 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14
0e 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21
As we can see, an out-of-bounds write operation occurred. Looking at the pseudo-code of this vulnerable function we can see that:
Line 1 __int64 __fastcall TIF_decode_thunderscan(__int64 a1, unsigned int a2, char *a3)
Line 2 {
Line 3 unsigned int currentOffset; // ebp
Line 4 char v4; // si
Line 5 char *dstBuffer; // rbx
Line 6 unsigned int value_div2; // er15
Line 7 struct_a1 *v7; // r13
Line 8 unsigned int loopCounter; // er14
Line 9 unsigned int valFromFile; // er12
Line 10 char _readByteFromFile; // r8
Line 11 char v11; // al
Line 12 signed int v12; // edx
Line 13 int v13; // eax
Line 14 char v14; // si
Line 15 char v15; // al
Line 16 signed int loopLimit; // edx
Line 17 int x; // eax
Line 18 char v18; // si
Line 19 char v19; // al
Line 20 char v20; // si
Line 21 char *v21; // rdi
Line 22 unsigned __int64 v22; // rdx
Line 23 char readByteFromFile; // [rsp+58h] [rbp+10h]
Line 24
Line 25 currentOffset = 0;
Line 26 v4 = 0;
Line 27 dstBuffer = a3;
Line 28 value_div2 = a2;
Line 29 v7 = (struct_a1 *)a1;
Line 30 loopCounter = 0;
Line 31 if ( !a2 )
Line 32 return 0i64;
Line 33 valFromFile = 2 * a2;
Line 34 do
Line 35 {
Line 36 if ( currentOffset >= valFromFile || !(unsigned int)IOb_byte_read(v7, &readByteFromFile) )
Line 37 break;
Line 38 _readByteFromFile = readByteFromFile;
Line 39 if ( readByteFromFile & 0xC0 )
Line 40 {
Line 41 switch ( readByteFromFile & 0xC0 )
Line 42 {
Line 43 (...)
Line 44 }
Line 45 }
Line 46 else
Line 47 {
Line 48 (...)
Line 49 if ( _readByteFromFile > 0 )
Line 50 {
Line 51 v21 = dstBuffer;
Line 52 v22 = (unsigned __int8)(((unsigned __int8)(_readByteFromFile - 1) >> 1) + 1);
Line 53 dstBuffer += v22;
Line 54 memset(v21, v20, v22);
Line 55 _readByteFromFile += -2 * v22;
Line 56 readByteFromFile = _readByteFromFile;
Line 57 }
Line 58 if ( _readByteFromFile == -1 )
Line 59 {
Line 60 --dstBuffer;
Line 61 *dstBuffer &= 0xF0u;
Line 62 }
Line 63 v4 = v20 & 0xF;
Line 64 }
Line 65 ++loopCounter;
Line 66 }
Line 67 while ( loopCounter < value_div2 );
Line 68 return 0i64;
Line 69 }
A while loop is controlled via the value_div2 variable. Its value is equal to 1 and comes from a value read directly from the file at offset 0xAA (IFD->ENT->value [2 bytes size]) and later divided by 2 (the value can not be smaller than 1 so for 1 the result is 1 and for 2 and 3 it is also 1). dstBuffer is also allocated based on that value :
0:000> !heap -p -a 0x000002b99906eff0
address 000002b99906eff0 found in
_DPH_HEAP_ROOT @ 2b9919a1000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
2b99963a000: 2b99906eff0 1 - 2b99906e000 2000
00007ff85bf7f4bf ntdll!RtlDebugAllocateHeap+0x000000000000003f
00007ff85bf2b530 ntdll!RtlpAllocateHeap+0x000000000008f760
00007ff85be99725 ntdll!RtlpAllocateHeapInternal+0x00000000000005e5
00007ff838cc6407 MSVCR110!malloc+0x000000000000005b
00007ff81d21781b igCore19d!AF_memm_alloc+0x000000000000002b
00007ff81d35e613 igCore19d!IG_mpi_page_set+0x000000000013f473
00007ff81d35df56 igCore19d!IG_mpi_page_set+0x000000000013edb6
00007ff81d362867 igCore19d!IG_mpi_page_set+0x00000000001436c7
00007ff81d35b154 igCore19d!IG_mpi_page_set+0x000000000013bfb4
00007ff81d1ed704 igCore19d!IG_image_savelist_get+0x0000000000000ef4
00007ff81d236c99 igCore19d!IG_mpi_page_set+0x0000000000017af9
00007ff81d23653c igCore19d!IG_mpi_page_set+0x000000000001739c
00007ff81d1be7c0 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
During each loop cycle a byte is read from the file at line 36 into the variable readByteFromFile starting from offset:
0 + sizeof(IFH) == 8
Next based on the value of readByteFromFile a proper action is taken. The vulnerability appears if value of readByteFromFile is in the range <1;63> and its value after specific calculations (((i - 1)/ 2 ) + 1) is bigger than the value used for dstBuffer allocation. In our case the capacity of dstBuffer is 1 and the byte read from offset 0x8 is equal to 0x3F. Passing 0x3F value into above formula we obtain a value equal to 0x20. That value is used in a memcpy operation as a size argument.
Below code generating possible pair values :
>>> for i in range(0,255):
if not (i & 0xC0):
print "Read byte : 0x{0:X} - memcpy size arg : 0x{1:X}".format(i,(((i - 1)/ 2 ) + 1))
(...)
Read byte : 0x3F - memcpy size arg : 0x20
All these circumstances lead to an out of bounds write which can allow an attacker to gain remote code execution.
Crash Information
0:000> !analyze -v
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************
DUMP_CLASS: 2
DUMP_QUALIFIER: 0
MODLIST_WITH_TSCHKSUM_HASH: e6311aced106b626c7f0882ce5e44d10d5417c9e
MODLIST_SHA1_HASH: 65c7eea70564f728d27c5e9e2c5360b823e6fec9
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+1402e0
00007ff8`1d35f480 f3aa rep stos byte ptr [rdi]
EXCEPTION_RECORD: (.exr -1)
ExceptionAddress: 00007ff81d35f480 (igCore19d!IG_mpi_page_set+0x00000000001402e0)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 0000000000000001
Parameter[1]: 000002b99906f000
Attempt to write to address 000002b99906f000
FAULTING_THREAD: 000008ec
PROCESS_NAME: igFuzzer.exe
FOLLOWUP_IP:
igCore19d!IG_mpi_page_set+1402e0
00007ff8`1d35f480 f3aa rep stos byte ptr [rdi]
WRITE_ADDRESS: 000002b99906f000
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: 0000000000000001
EXCEPTION_PARAMETER2: 000002b99906f000
WATSON_BKT_PROCSTAMP: 5d26f980
WATSON_BKT_MODULE: igCore19d.dll
WATSON_BKT_MODSTAMP: 5c101edf
WATSON_BKT_MODOFFSET: 1bf480
WATSON_BKT_MODVER: 19.3.0.0
MODULE_VER_PRODUCT: Accusoft ImageGear
BUILD_VERSION_STRING: 17134.1.amd64fre.rs4_release.180410-1804
ANALYSIS_SESSION_HOST: ICELENOVO
ANALYSIS_SESSION_TIME: 07-19-2019 07:14:24.0934
ANALYSIS_VERSION: 10.0.18914.1001 amd64fre
THREAD_ATTRIBUTES:
OS_LOCALE: ENU
BUGCHECK_STR: APPLICATION_FAULT_INVALID_POINTER_WRITE_AVRF
DEFAULT_BUCKET_ID: INVALID_POINTER_WRITE_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: [0x8ec]
Frame: [0] : igCore19d!IG_mpi_page_set
ID: [0n286]
Type: [INVALID_POINTER_WRITE]
Class: Primary
Scope: DEFAULT_BUCKET_ID (Failure Bucket ID prefix)
BUCKET_ID
Name: Add
Data: Omit
PID: [Unspecified]
TID: [0x8ec]
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: [0x1be0]
TID: [0x8ec]
Frame: [0] : igCore19d!IG_mpi_page_set
LAST_CONTROL_TRANSFER: from 00007ff81d35e760 to 00007ff81d35f480
STACK_TEXT:
000000fe`6a1be940 00007ff8`1d35e760 : 000000fe`6a1beba0 00007ff8`1d35f23f 000002b9`99068ff0 00000000`00000000 : igCore19d!IG_mpi_page_set+0x1402e0
000000fe`6a1be990 00007ff8`1d35df56 : 000000fe`6a1bf240 00000000`1000001e 00000000`00000003 000002b9`98c15d50 : igCore19d!IG_mpi_page_set+0x13f5c0
000000fe`6a1bea60 00007ff8`1d362867 : 00000000`00000004 000000fe`6a1bf240 00000000`00000000 000002b9`000003e8 : igCore19d!IG_mpi_page_set+0x13edb6
000000fe`6a1beae0 00007ff8`1d35b154 : 00000000`00000001 000002b9`98c15d50 00000000`1000001e 000000fe`6a1bed10 : igCore19d!IG_mpi_page_set+0x1436c7
000000fe`6a1beb50 00007ff8`1d1ed704 : 000002b9`92488ff0 00000000`00000004 00000000`00000000 00007ff8`00000037 : igCore19d!IG_mpi_page_set+0x13bfb4
000000fe`6a1bf150 00007ff8`1d236c99 : 00000000`00000028 000000fe`6a1bf768 000000fe`6a1bf240 00000000`00000000 : igCore19d!IG_image_savelist_get+0xef4
000000fe`6a1bf1d0 00007ff8`1d23653c : 000002b9`923bcfc8 00007ff8`1d22cc95 00000000`00000000 00000000`00000000 : igCore19d!IG_mpi_page_set+0x17af9
000000fe`6a1bf6b0 00007ff8`1d1be7c0 : 000002b9`989b8fa5 00007ff8`58519570 00000000`00000000 00007ff8`585180b0 : igCore19d!IG_mpi_page_set+0x1739c
000000fe`6a1bf6f0 00007ff6`33951112 : 00007ff8`58519570 00000000`00000000 00007ff8`58519570 000000fe`6a1bf798 : igCore19d!IG_load_file+0x80
000000fe`6a1bf740 00007ff6`3395122c : 000002b9`989b8fa5 000002b9`989b8fe8 000002b9`00000021 00000000`00001000 : igFuzzer!fuzzme+0x32
000000fe`6a1bf790 00007ff6`33951474 : 00000000`00000004 000002b9`989b8f70 00000000`00000000 00000000`00000000 : igFuzzer!main+0xbc
000000fe`6a1bf7d0 00007ff8`5b974034 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : igFuzzer!__scrt_common_main_seh+0x10c
000000fe`6a1bf810 00007ff8`5bef3691 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14
000000fe`6a1bf840 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21
STACK_COMMAND: ~0s ; .cxr ; kb
THREAD_SHA1_HASH_MOD_FUNC: 82a7bc6dd3104d830ca26855c5e07a6d00940aaa
THREAD_SHA1_HASH_MOD_FUNC_OFFSET: b0d9fe750119f0cca8352c6ddb9fc4142deab619
THREAD_SHA1_HASH_MOD: 252eeb5ab64e7dfeac0928d869d2e1c90b7990ce
FAULT_INSTR_CODE: 244aaf3
SYMBOL_STACK_INDEX: 0
SYMBOL_NAME: igCore19d!IG_mpi_page_set+1402e0
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: igCore19d
IMAGE_NAME: igCore19d.dll
DEBUG_FLR_IMAGE_TIMESTAMP: 5c101edf
FAILURE_BUCKET_ID: INVALID_POINTER_WRITE_AVRF_c0000005_igCore19d.dll!IG_mpi_page_set
BUCKET_ID: APPLICATION_FAULT_INVALID_POINTER_WRITE_AVRF_igCore19d!IG_mpi_page_set+1402e0
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: 1402e0
BUCKET_ID_MODTIMEDATESTAMP: 5c101edf
BUCKET_ID_MODCHECKSUM: 41928b
BUCKET_ID_MODVER_STR: 19.3.0.0
BUCKET_ID_PREFIX_STR: APPLICATION_FAULT_INVALID_POINTER_WRITE_AVRF_
FAILURE_PROBLEM_CLASS: APPLICATION_FAULT
FAILURE_SYMBOL_NAME: igCore19d.dll!IG_mpi_page_set
TARGET_TIME: 2019-07-19T10:14:30.000Z
OSBUILD: 17134
OSSERVICEPACK: 753
SERVICEPACK_NUMBER: 0
OS_REVISION: 0
OSPLATFORM_TYPE: x64
OSNAME: Windows 10
OSEDITION: Windows 10 WinNt SingleUserTS
USER_LCID: 0
OSBUILD_TIMESTAMP: unknown_date
BUILDDATESTAMP_STR: 180410-1804
BUILDLAB_STR: rs4_release
BUILDOSVER_STR: 10.0.17134.1.amd64fre.rs4_release.180410-1804
ANALYSIS_SESSION_ELAPSED_TIME: 1588
ANALYSIS_SOURCE: UM
FAILURE_ID_HASH_STRING: um:invalid_pointer_write_avrf_c0000005_igcore19d.dll!ig_mpi_page_set
FAILURE_ID_HASH: {39ff52ad-9054-81fd-3e4d-ef5d82e4b2c1}
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
Timeline
2019-07-30 - Vendor Disclosure
2019-11-27 - Vendor patched
2019-12-02 - Public Release
Discovered by Marcin Towalski & Marcin ‘Icewall’ Noga of Cisco Talos.