Talos Vulnerability Report


Accusoft ImageGear PNG pngread width code execution vulnerability

January 27, 2020
CVE Number



An exploitable out-of-bounds write vulnerability exists in the igcore19d.dll PNG pngread parser of the Accusoft ImageGear 19.5.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 victim to trigger the vulnerability.

Tested Versions

Accusoft ImageGear 19.5.0

Product URLs


CVSSv3 Score

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, DPNGM, PDF, Microsoft Office and others.

There is a vulnerability in the PNG raster image parser. A specially crafted PNG file can lead to an out-of-bounds write resulting in 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.
eax=00000004 ebx=00000002 ecx=0bbdfff8 edx=000001f4 esi=00000001 edi=fffffffe
eip=5b914006 esp=00afd480 ebp=00afd494 iopl=0         nv up ei pl nz ac po cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010213
5b914006 897cb104        mov     dword ptr [ecx+esi*4+4],edi ds:002b:0bbe0000=????????

Checking the capacity of the buffer pointed by ecx:

0:000> !heap -p -a 0x0bbdfff8
	address 0bbdfff8 found in
	_DPH_HEAP_ROOT @ cd1000
	in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
								 ba23374:          bbdfff8                8 -          bbdf000             2000
	5bbfab70 verifier!AVrfDebugPageHeapAllocate+0x00000240
	77378fcb ntdll!RtlDebugAllocateHeap+0x00000039
	772cbb0d ntdll!RtlpAllocateHeap+0x000000ed
	772cb02f ntdll!RtlpAllocateHeapInternal+0x0000022f
	772cadee ntdll!RtlAllocateHeap+0x0000003e
	5b56daff MSVCR110!malloc+0x00000049
	5b90582e igCore19d!AF_memm_alloc+0x0000001e
	5b91c428 igCore19d!IG_mpi_page_set+0x00011098
	5b8f84de igCore19d!IG_cpm_profiles_reset+0x0000dfae
	5b9f0c04 igCore19d!IG_mpi_page_set+0x000e5874
	5b9ee32c igCore19d!IG_mpi_page_set+0x000e2f9c
	5b8e07c9 igCore19d!IG_image_savelist_get+0x00000b29
	5b91fb97 igCore19d!IG_mpi_page_set+0x00014807
	5b91f4f9 igCore19d!IG_mpi_page_set+0x00014169
	5b8b6007 igCore19d!IG_load_file+0x00000047
	00ef59ac simple_exe_141+0x000159ac
	00ef61a7 simple_exe_141+0x000161a7
	00ef6cbe simple_exe_141+0x00016cbe
	00ef6b27 simple_exe_141+0x00016b27
	00ef69bd simple_exe_141+0x000169bd
	00ef6d38 simple_exe_141+0x00016d38
	74f56359 KERNEL32!BaseThreadInitThunk+0x00000019
	772f7b74 ntdll!__RtlUserThreadStart+0x0000002f
	772f7b44 ntdll!_RtlUserThreadStart+0x0000001b

We can see that it is equal to 8 bytes and that the calculated address in the crashing line points out of its range.

Further analysis revealed that space for that buffer is allocated in the following place:

Line 1 	int __thiscall sub_5DCCC3D0(char *this, int a2, char *a3)
Line 2 	{
Line 3 	  int v3; // ecx
Line 4 	  char *_this; // ST04_4
Line 5 	  size_t mem_size; // ST04_4
Line 6 	  int v6; // eax
Line 7 	  char *v8; // [esp+0h] [ebp-4h]
Line 8 
Line 9 	  v8 = this;
Line 10	  if ( !sub_5DC6C860(a3, (int)&v8) )
Line 11	  {
Line 12		**(_DWORD **)(a2 + 4) = v8;
Line 13		if ( sub_5DC684E0((int)a3) == 1 )
Line 14		{
Line 15		  _this = v8;
Line 16		  *(_DWORD *)(a2 + 100) = 1;
Line 17		  mem_size = 4 * getWidth(_this) + 16;
Line 18		  v6 = sub_5DC6CFD0(v8);
Line 19		  *(_DWORD *)(a2 + 104) = AF_memm_alloc(v6, mem_size, (int)"..\\..\\..\\..\\Common\\Io\\dfltldcb.c", 162);
Line 20		}
Line 21	  }
Line 22	  return sub_5DC8AA00(v3);
Line 23	}

At line 17 we can see a mem_size calculation which is made based on value returned from getWidth function. This function returns [UINT]IDHR->Width value directly from file.

In our case its value is equal 0xfffffffe ( offset : 0x10 ). Doing the necessary math we end up with mem_size equal 0x00000008 = 0xfffffffe * 4 + 16.

With that in mind, let us return to vulnerable function:

Line 1 	int __cdecl sub_5DCC3F20(unsigned __int8 *a1, _DWORD *inBuffer, int inStore_Value)
Line 2 	{
Line 3 	  int store_value; // edi
Line 4 	  int index; // esi
Line 5 	  int v12; // ebx
Line 6 	  _DWORD *buffer; // ecx
Line 7 	  int result; // eax
Line 8 	  int v15; // [esp+Ch] [ebp-8h]
Line 9 	  signed int __i; // [esp+10h] [ebp-4h]
Line 10
Line 11	  store_value = inStore_Value;
Line 12	  v4 = 0;
Line 13	  for ( __i = 1; ; __i += 2 )
Line 14	  {
Line 15		(...)
Line 16	  }
Line 17	  buffer = inBuffer;
Line 18	  result = index + 3;
Line 19	  inBuffer[index] = store_value;
Line 20	  buffer[index + 1] = store_value;              // !!! VULN !!!
Line 21	  buffer[index + 2] = store_value;
Line 22	  *buffer = index + 3;
Line 23	  return result;
Line 24	}

In the above pseudo-code we can see that there is no check whether buffer size is bigger-equal than 12 bytes and because of that at line 20 an out-of-bounds write occurs. As we can see, an attacker controls all presented variables just by proper file content manipulation.

Increasing the loop count via the v81 variable, an attacker can cause an out-of-bounds write leading to memory corruption which can result in remote code execution.

Crash Information

eax=00000004 ebx=00000002 ecx=27095ff8 edx=000001f4 esi=00000001 edi=fffffffe
eip=5dcc4006 esp=0100d2b0 ebp=0100d2c4 iopl=0         nv up ei pl nz ac po cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000213
5dcc4006 897cb104        mov     dword ptr [ecx+esi*4+4],edi ds:002b:27096000=????????
0:000> !analyze -v
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *


	Key  : AV.Fault
	Value: Read

	Key  : Analysis.CPU.Sec
	Value: 0

	Key  : Analysis.DebugAnalysisProvider.CPP
	Value: Create: 8007007e on DESKTOP-E4N8506

	Key  : Analysis.DebugData
	Value: CreateObject

	Key  : Analysis.DebugModel
	Value: CreateObject

	Key  : Analysis.Elapsed.Sec
	Value: 0

	Key  : Analysis.Memory.CommitPeak.Mb
	Value: 433

	Key  : Analysis.System
	Value: CreateObject

	Key  : Timeline.OS.Boot.DeltaSec
	Value: 541573



ExceptionAddress: 5dcc4006 (igCore19d!IG_mpi_page_set+0x00008c76)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000000
   Parameter[1]: 27096000
Attempt to read from address 27096000


PROCESS_NAME:  igFuzzer.exe

READ_ADDRESS:  27096000 

ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.




WARNING: Stack unwind information not available. Following frames may be wrong.
0100d2c4 5dcc48b1 26e75ff8 27095ff8 fffffffe igCore19d!IG_mpi_page_set+0x8c76
0100d2d8 5dccc969 26e75ff8 27095ff8 fffffffe igCore19d!IG_mpi_page_set+0x9521
0100d2f8 5dca94fe 0100f674 26e75ff8 00000000 igCore19d!IG_mpi_page_set+0x115d9
0100d318 5dd9e0c9 0100f500 26e75ff8 00000000 igCore19d!IG_cpm_profiles_reset+0xefce
0100d334 5dd9fe7f 257e9fa8 0100f500 26e75ff8 igCore19d!IG_mpi_page_set+0xe2d39
0100efb0 5dda0c74 0100f500 1000001b 2469dfe8 igCore19d!IG_mpi_page_set+0xe4aef
0100efe4 5dd9e32c 0100f500 1000001b 2469dfe8 igCore19d!IG_mpi_page_set+0xe58e4
0100f478 5dc907c9 0100f500 2469dfe8 00000001 igCore19d!IG_mpi_page_set+0xe2f9c
0100f4b0 5dccfb97 00000000 2469dfe8 0100f500 igCore19d!IG_image_savelist_get+0xb29
0100f72c 5dccf4f9 00000000 20f04f98 00000001 igCore19d!IG_mpi_page_set+0x14807
0100f74c 5dc66007 00000000 20f04f98 00000001 igCore19d!IG_mpi_page_set+0x14169
0100f76c 00ef59ac 20f04f98 0100f858 0100f87c igCore19d!IG_load_file+0x47
0100f86c 00ef61a7 20f04f98 0100f9a0 00000021 igFuzzer+0x159ac
0100fa38 00ef6cbe 00000005 1a420f40 1a287f40 igFuzzer+0x161a7
0100fa4c 00ef6b27 997563fa 00ef15e1 00ef15e1 igFuzzer+0x16cbe
0100faa8 00ef69bd 0100fab8 00ef6d38 0100fac8 igFuzzer+0x16b27
0100fab0 00ef6d38 0100fac8 74f56359 00d55000 igFuzzer+0x169bd
0100fab8 74f56359 00d55000 74f56340 0100fb24 igFuzzer+0x16d38
0100fac8 772f7b74 00d55000 aaeb7cb0 00000000 KERNEL32!BaseThreadInitThunk+0x19
0100fb24 772f7b44 ffffffff 77318f13 00000000 ntdll!__RtlUserThreadStart+0x2f
0100fb34 00000000 00ef15e1 00d55000 00000000 ntdll!_RtlUserThreadStart+0x1b

STACK_COMMAND:  ~0s ; .cxr ; kb

SYMBOL_NAME:  igCore19d!IG_mpi_page_set+8c76

MODULE_NAME: igCore19d

IMAGE_NAME:  igCore19d.dll

FAILURE_BUCKET_ID:  INVALID_POINTER_READ_AVRF_c0000005_igCore19d.dll!IG_mpi_page_set


OSNAME:  Windows 8

FAILURE_ID_HASH:  {bfd6b5ab-5824-8327-06e6-1c2f38a120f0}

Followup:     MachineOwner

0:000> lmva eip
Browse full module list
start    end        module name
5b8a0000 5bbe9000   igCore19d   (export symbols)       d:\projects\ImageGear\current\Build\Bin\x86\igCore19d.dll
	Loaded symbol image file: d:\projects\ImageGear\current\Build\Bin\x86\igCore19d.dll
	Image path: d:\projects\ImageGear\current\Build\Bin\x86\igCore19d.dll
	Image name: igCore19d.dll
	Browse all global symbols  functions  data
	Timestamp:        Fri Nov 22 15:45:29 2019 (5DD7F489)
	CheckSum:         00356062
	ImageSize:        00349000
	File version:
	Product version:
	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
		FileDescription:  Accusoft ImageGear CORE DLL 
		LegalCopyright:   Copyright 1996-2019 Accusoft Corporation. All rights reserved.
		LegalTrademarks:  ImageGearÆ and AccusoftÆ are registered trademarks of Accusoft Corporation


2020-01-27 - Vendor Disclosure
2020-02-10 - Public Release


Discovered by Emmanuel Tacheau and a member of Cisco Talos.