CVE-2023-32614
A heap-based buffer overflow vulnerability exists in the create_png_object functionality of Accusoft ImageGear 20.1. A specially crafted malformed file can lead to memory corruption. An attacker can provide a malicious file to trigger this vulnerability.
The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.
Accusoft ImageGear 20.1
ImageGear - https://www.accusoft.com/products/imagegear-collection/
7.0 - CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:L
CWE-124 - Buffer Underwrite (‘Buffer Underflow’)
The ImageGear library is a document-imaging developer toolkit that offers image conversion, creation, editing, annotation and more. It supports more than 100 formats such as DICOM, PDF, Microsoft Office and others.
A specially crafted PNG file can lead to a heap-based buffer overflow in create_png_object
, due to an invalid unsigned comparison.
Trying to load a malformed PNG, we end up in the following situation:
Time Travel Position: 2A89A0:0
eax=fffff007 ebx=0c410fe8 ecx=00000000 edx=0d03eff8 esi=00000000 edi=00000001
eip=75174029 esp=0019da90 ebp=0019f6f0 iopl=0 nv up ei ng nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000282
igCore20d!IG_mpi_page_set+0xe7f09:
75174029 c6040200 mov byte ptr [edx+eax],0 ds:002b:0d03dfff=??
Windbg preview struggles a bit with heap command and trace. As we’re debugging with pageheap enabled, we can perform heap investigation and parse heap metadata with _DPH_BLOCK_INFORMATION
. This structure is helpful to get stack trace and details about heap allocation.
0:000> dt _DPH_BLOCK_INFORMATION edx-0x20
ntdll!_DPH_BLOCK_INFORMATION
+0x000 StartStamp : 0
+0x004 Heap : (null)
+0x008 RequestedSize : 0
+0x00c ActualSize : 0
+0x010 FreeQueue : _LIST_ENTRY [ 0x0 - 0x0 ]
+0x010 FreePushList : _SINGLE_LIST_ENTRY
+0x010 TraceIndex : 0
+0x018 StackTrace : (null)
+0x01c EndStamp : 0
The metadata are null, so we need to investigate more into the function causing the crash, represented with the following pseudo-code :
LINE1 BOOLEAN create_png_object(mys_table_function *mys_table_function,uint kind_of_heap,int param_3,
LINE2 PNG_object *png_object,PNG_object *png_object_2,HIGDIBINFO higdibinfo,
LINE3 undefined4 param_7,undefined4 param_8)
LINE4
LINE5 {
LINE6 ulonglong uVar1;
LINE7 uint **ppuVar2;
LINE8 uint *size;
LINE9 size_t size_malloc;
LINE10 ushort uVar3;
LINE11 BOOLEAN BVar4;
LINE12 undefined4 *puVar5;
LINE13 dword dVar6;
LINE14 void *pvVar7;
LINE15 AT_DIMENSION AVar8;
LINE16 int iVar9;
LINE17 undefined *puVar10;
LINE18 uint *puVar11;
LINE19 byte bVar12;
LINE20 uint uVar13;
LINE21 uint *puVar14;
LINE22 uint uVar15;
LINE23 png_to_be_defined *ppVar16;
LINE24 byte bVar17;
LINE25 uint uVar18;
LINE26 size_t sVar19;
LINE27 uint *puVar20;
LINE28 int _bits_qtity;
LINE29 HIGDIBINFO _higdibinfo;
LINE30 int *piVar21;
LINE31 AT_INT lplValue2;
LINE32 char *lpExtraText;
LINE33 UINT local_1c44;
LINE34 int local_1c40;
LINE35 uint local_1c3c;
LINE36 uint local_1c38;
LINE37 uint **local_1c34;
LINE38 png_to_be_defined *local_1c30;
LINE39 uint *local_1c2c;
LINE40 uint *local_1c28;
LINE41 uint local_1c24;
LINE42 int local_1c20;
LINE43 PNG_object *_png_object;
LINE44 void *local_1c18;
LINE45 HIGDIBINFO __higdibinfo;
LINE46 undefined4 *local_1c10;
LINE47 int *local_1c0c;
LINE48 dword __size_buffer;
LINE49 void *local_1c04;
LINE50 uint local_1c00;
LINE51 void *__p_buffer;
LINE52 uint _kind_of_heap;
LINE53 uint *_size_from_colortype_withd;
LINE54 uint local_1bf0;
LINE55 undefined4 local_1bec;
LINE56 int local_1620;
LINE57 undefined4 local_28;
LINE58 undefined4 local_24;
LINE59 undefined4 local_20;
LINE60 undefined4 local_1c;
LINE61 undefined4 local_18;
LINE62 undefined4 local_14;
LINE63 undefined4 local_10;
LINE64 undefined4 local_c;
LINE65 uint local_8;
LINE66
LINE67 local_8 = DAT_10319e74 ^ (uint)&stack0xfffffffc;
LINE68 _kind_of_heap = kind_of_heap;
LINE69 _png_object = png_object;
LINE70 __higdibinfo = higdibinfo;
LINE71 local_28 = 0x200000f;
LINE72 local_24 = 0x1000100;
LINE73 local_20 = 0x4000f;
LINE74 local_1c = 0x10002;
LINE75 local_10 = 0x404040f;
LINE76 local_c = 0x1010202;
LINE77 local_18 = 0x408080f;
LINE78 local_14 = 0x1020204;
LINE79 local_1c00 = 0;
LINE80 local_1c44 = 0;
LINE81 local_1c30 = (png_to_be_defined *)AF_memm_alloc(kind_of_heap,0x60);
LINE82 if (local_1c30 == (png_to_be_defined *)0x0) {
LINE83 AF_err_record_set("..\\..\\..\\..\\Common\\Formats\\pngread.c",0xb78,-1000,0,0x60,kind_of_heap,
LINE84 (LPCHAR)0x0);
LINE85 BVar4 = @__security_check_cookie@4();
LINE86 return BVar4;
LINE87 }
LINE88 _size_from_colortype_withd = (uint *)png_compute_size_from_color_and_width(png_object);
LINE89 sVar19 = (int)_size_from_colortype_withd + (0x40 - ((uint)_size_from_colortype_withd & 0x3f));
LINE90 local_1c04 = AF_memm_alloc(kind_of_heap,sVar19);
LINE91 if (local_1c04 == (void *)0x0) {
LINE92 AF_memm_free_all(kind_of_heap);
LINE93 AF_err_record_set("..\\..\\..\\..\\Common\\Formats\\pngread.c",0xb86,-1000,0,
LINE94 (AT_INT)_size_from_colortype_withd,kind_of_heap,(LPCHAR)0x0);
LINE95 BVar4 = @__security_check_cookie@4();
LINE96 return BVar4;
LINE97 }
LINE98 puVar5 = (undefined4 *)AF_memm_alloc(kind_of_heap,sVar19);
LINE99 local_1c10 = puVar5;
LINE100 if (puVar5 == (undefined4 *)0x0) {
LINE101 AF_memm_free_all(kind_of_heap);
LINE102 AF_err_record_set("..\\..\\..\\..\\Common\\Formats\\pngread.c",0xb90,-1000,0,
LINE103 (AT_INT)_size_from_colortype_withd,kind_of_heap,(LPCHAR)0x0);
LINE104 BVar4 = @__security_check_cookie@4();
LINE105 return BVar4;
LINE106 }
LINE107 local_1c18 = AF_memm_alloc(kind_of_heap,(size_t)_size_from_colortype_withd);
LINE108 if (local_1c18 == (void *)0x0) {
LINE109 AF_memm_free_all(kind_of_heap);
LINE110 AF_err_record_set("..\\..\\..\\..\\Common\\Formats\\pngread.c",0xb99,-1000,0,
LINE111 (AT_INT)_size_from_colortype_withd,kind_of_heap,(LPCHAR)0x0);
LINE112 BVar4 = @__security_check_cookie@4();
LINE113 return BVar4;
LINE114 }
LINE115 if (_size_from_colortype_withd != (uint *)0x0) {
LINE116 for (uVar13 = (uint)_size_from_colortype_withd >> 2; uVar13 != 0; uVar13 = uVar13 - 1) {
LINE117 *puVar5 = 0;
LINE118 puVar5 = puVar5 + 1;
LINE119 }
LINE120 for (uVar13 = (uint)_size_from_colortype_withd & 3; kind_of_heap = _kind_of_heap, uVar13 != 0;
LINE121 uVar13 = uVar13 - 1) {
LINE122 *(undefined *)puVar5 = 0;
LINE123 puVar5 = (undefined4 *)((int)puVar5 + 1);
LINE124 }
LINE125 }
LINE126 dVar6 = raster_size_from_HIGDIBINFO(higdibinfo);
LINE127 __size_buffer = dVar6;
LINE128 pvVar7 = AF_memm_alloc(kind_of_heap,dVar6);
LINE129 uVar13 = _kind_of_heap;
LINE130 __p_buffer = pvVar7;
LINE131 if (pvVar7 == (void *)0x0) {
LINE132 AF_memm_free_all(_kind_of_heap);
LINE133 AF_err_record_set("..\\..\\..\\..\\Common\\Formats\\pngread.c",0xba7,-1000,0,dVar6,uVar13,
LINE134 (LPCHAR)0x0);
LINE135 BVar4 = @__security_check_cookie@4();
LINE136 return BVar4;
LINE137 }
LINE138 OS_memset(&local_1bf0,0,0x1bc8);
LINE139 local_1bec = 2;
LINE140 if (_png_object->InterlaceType == 1) {
LINE141 AVar8 = DIB_height_get(higdibinfo);
LINE142 _bits_qtity = (int)((AVar8 + 7 >> 0x1f & 7U) + AVar8 + 7) >> 3;
LINE143 uVar13 = _bits_qtity * 4;
LINE144 if (0xffff < (int)uVar13) {
LINE145 lpExtraText = "Interlaced png image has too big heght. Can\'t load image.";
LINE146 lplValue2 = 0;
LINE147 AVar8 = DIB_height_get(higdibinfo);
LINE148 AF_err_record_set("..\\..\\..\\..\\Common\\Formats\\pngread.c",0xbcd,-0x3ed,0,AVar8,lplValue2,
LINE149 lpExtraText);
LINE150 BVar4 = @__security_check_cookie@4();
LINE151 return BVar4;
LINE152 }
LINE153 if (0xffff < dVar6) {
LINE154 AF_err_record_set("..\\..\\..\\..\\Common\\Formats\\pngread.c",0xbd3,-0x3ed,0,dVar6,0,
LINE155 "Interlaced png image has too big raster size. Can\'t load image.");
LINE156 BVar4 = @__security_check_cookie@4();
LINE157 return BVar4;
LINE158 }
LINE159 sVar19 = _bits_qtity * 0x10;
LINE160 local_1c0c = (int *)AF_memm_alloc(_kind_of_heap,sVar19);
LINE161 size_malloc = __size_buffer;
LINE162 if (local_1c0c == (int *)0x0) {
LINE163 AF_err_record_set("..\\..\\..\\..\\Common\\Formats\\pngread.c",0xbdc,-1000,0,sVar19,
LINE164 _kind_of_heap,(LPCHAR)0x0);
LINE165 BVar4 = @__security_check_cookie@4();
LINE166 return BVar4;
LINE167 }
LINE168 puVar14 = (uint *)(uVar13 & 0xffff);
LINE169 puVar20 = (uint *)0x0;
LINE170 if (puVar14 != (uint *)0x0) {
LINE171 do {
LINE172 pvVar7 = AF_memm_alloc(_kind_of_heap,size_malloc);
LINE173 local_1c0c[(int)puVar20] = (int)pvVar7;
LINE174 if (pvVar7 == (void *)0x0) {
LINE175 AF_err_record_set("..\\..\\..\\..\\Common\\Formats\\pngread.c",0xbe6,-1000,0,size_malloc,
LINE176 _kind_of_heap,(LPCHAR)0x0);
LINE177 BVar4 = @__security_check_cookie@4();
LINE178 return BVar4;
LINE179 }
LINE180 puVar20 = (uint *)((int)puVar20 + 1);
LINE181 puVar11 = puVar14;
LINE182 piVar21 = local_1c0c;
LINE183 } while (puVar20 < puVar14);
LINE184 for (; puVar11 != (uint *)0x0; puVar11 = (uint *)((int)puVar11 + -1)) {
LINE185 uVar13 = 0;
LINE186 if (__size_buffer != 0) {
LINE187 do {
LINE188 uVar15 = uVar13 + 1;
LINE189 *(undefined *)(uVar13 + *piVar21) = 0xff;
LINE190 uVar13 = uVar15;
LINE191 } while (uVar15 < __size_buffer);
LINE192 }
LINE193 higdibinfo = __higdibinfo;
LINE194 piVar21 = piVar21 + 1;
LINE195 }
LINE196 }
LINE197 fill_some_object_from_PNG_Object(local_1c30,_png_object);
LINE198 local_1c20 = 1;
LINE199 ppVar16 = local_1c30;
LINE200 do {
LINE201 local_1c40 = (int)(short)local_1c20;
LINE202 local_1c34 = (uint **)((int)&ppVar16->field_0x0 + local_1c40 * 0xc);
LINE203 if (*(int *)(&ppVar16->field_0x8 + local_1c40 * 0xc) != 0) {
LINE204 if (_size_from_colortype_withd != (uint *)0x0) {
LINE205 puVar5 = local_1c10;
LINE206 for (uVar13 = (uint)_size_from_colortype_withd >> 2; uVar13 != 0; uVar13 = uVar13 - 1) {
LINE207 *puVar5 = 0;
LINE208 puVar5 = puVar5 + 1;
LINE209 }
LINE210 for (uVar13 = (uint)_size_from_colortype_withd & 3; uVar13 != 0; uVar13 = uVar13 - 1) {
LINE211 *(undefined *)puVar5 = 0;
LINE212 puVar5 = (undefined4 *)((int)puVar5 + 1);
LINE213 }
LINE214 }
LINE215 local_1c2c = *local_1c34;
LINE216 puVar11 = local_1c34[1];
LINE217 puVar20 = (uint *)0x0;
LINE218 local_1c24 = (uint)*(byte *)((int)&local_28 + local_1c40);
LINE219 local_1c00 = (uint)*(ushort *)&_png_object->Height;
LINE220 ppuVar2 = local_1c34;
LINE221 while ((local_1c34 = ppuVar2, 0 < (int)puVar11 &&
LINE222 (dVar6 = FUN_10154370(mys_table_function,&local_1bf0,local_1c04,local_1c2c),
LINE223 pvVar7 = __p_buffer, dVar6 == 0))) {
LINE224 uVar1 = (ulonglong)((int)local_1c2c - 1) / ZEXT48(ppuVar2[2]);
LINE225 uVar13 = (uint)uVar1 & 0xff;
LINE226 if ((char)uVar1 == '\0') {
LINE227 uVar13 = 1;
LINE228 }
LINE229 local_1c3c = local_1c3c & 0xffffff00 | uVar13;
LINE230 png_process_colortype
LINE231 (local_1c04,local_1c10,__p_buffer,0,local_1c2c,__size_buffer,local_1c3c,
LINE232 png_object_2,param_8,*(undefined4 *)(param_3 + 0x10));
LINE233 if ((short)local_1c20 < 7) {
LINE234 uVar15 = (uint)*(byte *)((int)&local_20 + local_1c40);
LINE235 local_1c28 = (uint *)0x0;
LINE236 local_1c38 = uVar15;
LINE237 if (local_1c34[2] != (uint *)0x0) {
LINE238 bVar12 = *(byte *)((int)&local_18 + local_1c40);
LINE239 _higdibinfo = __higdibinfo;
LINE240 do {
LINE241 puVar20 = local_1c28;
LINE242 local_1c38 = uVar15;
LINE243 dVar6 = IGDIBStd::DIB_bit_depth_get(_higdibinfo);
LINE244 if ((int)dVar6 < 0x11) {
LINE245 dVar6 = IGDIBStd::DIB_bit_depth_get(_higdibinfo);
LINE246 uVar18 = local_1c38;
LINE247 uVar15 = (dVar6 & 0xff) * (int)puVar20;
LINE248 _bits_qtity = uVar15 + ((int)uVar15 >> 0x1f & 7U);
LINE249 iVar9 = _bits_qtity >> 3;
LINE250 bVar17 = (byte)(dVar6 & 0xff);
LINE251 if (bVar17 < 9) {
LINE252 uVar15 = uVar15 & 0x80000007;
LINE253 if ((int)uVar15 < 0) {
LINE254 uVar15 = (uVar15 - 1 | 0xfffffff8) + 1;
LINE255 }
LINE256 uVar3 = ((ushort)*(byte *)(iVar9 + (int)__p_buffer) << ((byte)uVar15 & 0x1f) &
LINE257 0xff) >> (8 - bVar17 & 0x1f) & 0xff;
LINE258 }
LINE259 else {
LINE260 uVar3 = *(ushort *)((int)__p_buffer + (iVar9 - (_bits_qtity >> 0x1f) >> 1) * 2);
LINE261 }
LINE262 uVar15 = local_1c38;
LINE263 dVar6 = IGDIBStd::DIB_bit_depth_get(_higdibinfo);
LINE264 FUN_101572a0(local_1c0c[local_1c24],(byte)dVar6,uVar15,uVar3);
LINE265 local_1c28 = puVar20;
LINE266 }
LINE267 else {
LINE268 _bits_qtity = 0;
LINE269 uVar18 = uVar15;
LINE270 _higdibinfo = __higdibinfo;
LINE271 if (uVar13 != 0) {
LINE272 puVar10 = (undefined *)((int)local_1c28 * uVar13 + (int)__p_buffer);
LINE273 do {
LINE274 iVar9 = local_1c0c[local_1c24] + _bits_qtity;
LINE275 _bits_qtity = _bits_qtity + 1;
LINE276 *(undefined *)(iVar9 + uVar15 * uVar13) = *puVar10;
LINE277 puVar10 = puVar10 + 1;
LINE278 uVar18 = local_1c38;
LINE279 } while (_bits_qtity < (int)uVar13);
LINE280 }
LINE281 }
LINE282 uVar15 = bVar12 + uVar18 & 0xffff;
LINE283 local_1c28 = (uint *)((int)local_1c28 + 1);
LINE284 local_1c38 = uVar15;
LINE285 } while (local_1c28 < local_1c34[2]);
LINE286 }
LINE287 local_1c24 = (uint)(ushort)((ushort)*(byte *)((int)&local_10 + local_1c40) +
LINE288 (short)local_1c24);
LINE289 higdibinfo = __higdibinfo;
LINE290 puVar20 = local_1c28;
LINE291 }
LINE292 else if (local_1c00 != 0) {
LINE293 dVar6 = __size_buffer;
LINE294 AVar8 = DIB_height_get(higdibinfo);
LINE295 dVar6 = FUN_101523a0(higdibinfo,mys_table_function,local_1c0c[(int)puVar20],
LINE296 AVar8 - local_1c00,dVar6);
LINE297 if (dVar6 != 0) break;
LINE298 puVar20 = (uint *)((int)puVar20 + 1);
LINE299 local_1c00 = local_1c00 - 1;
LINE300 if (local_1c00 != 0) {
LINE301 dVar6 = __size_buffer;
LINE302 AVar8 = DIB_height_get(higdibinfo);
LINE303 dVar6 = FUN_101523a0(higdibinfo,mys_table_function,(int)pvVar7,AVar8 - local_1c00,
LINE304 dVar6);
LINE305 if (dVar6 != 0) break;
LINE306 local_1c00 = local_1c00 - 1;
LINE307 }
LINE308 }
LINE309 size = local_1c2c;
LINE310 OS_memcpy(local_1c18,local_1c04,(size_t)local_1c2c);
LINE311 OS_memcpy(local_1c04,local_1c10,(size_t)size);
LINE312 OS_memcpy(local_1c10,local_1c18,(size_t)size);
LINE313 puVar11 = (uint *)((int)puVar11 + -1);
LINE314 ppuVar2 = local_1c34;
LINE315 }
LINE316 BVar4 = AF_error_check();
LINE317 ppVar16 = local_1c30;
LINE318 if (BVar4 != False) break;
LINE319 }
LINE320 local_1c20 = local_1c20 + 1;
LINE321 } while ((short)local_1c20 < 8);
LINE322 _bits_qtity = local_1c20;
LINE323 BVar4 = AF_error_check();
LINE324 piVar21 = local_1c0c;
LINE325 if (((BVar4 == False) && (6 < (short)_bits_qtity)) && (local_1c00 == 1)) {
LINE326 dVar6 = __size_buffer;
LINE327 AVar8 = DIB_height_get(higdibinfo);
LINE328 piVar21 = local_1c0c;
LINE329 FUN_101523a0(higdibinfo,mys_table_function,local_1c0c[(int)puVar20],AVar8 + -1,dVar6);
LINE330 }
LINE331 puVar20 = (uint *)0x0;
LINE332 if (puVar14 != (uint *)0x0) {
LINE333 do {
LINE334 if (piVar21[(int)puVar20] != 0) {
LINE335 kind_of_error_handle
LINE336 (_kind_of_heap,piVar21[(int)puVar20],
LINE337 "..\\..\\..\\..\\Common\\Formats\\pngread.c",0xcac);
LINE338 }
LINE339 puVar20 = (uint *)((int)puVar20 + 1);
LINE340 } while (puVar20 < puVar14);
LINE341 }
LINE342 uVar13 = _kind_of_heap;
LINE343 kind_of_error_handle(_kind_of_heap,piVar21,"..\\..\\..\\..\\Common\\Formats\\pngread.c",0xcb0);
LINE344 pvVar7 = __p_buffer;
LINE345 }
LINE346 else {
LINE347 _bits_qtity = 0;
LINE348 uVar13 = _kind_of_heap;
LINE349 if (0 < (int)_png_object->Height) {
LINE350 while (dVar6 = FUN_10154370(mys_table_function,&local_1bf0,local_1c04,
LINE351 _size_from_colortype_withd), uVar13 = _kind_of_heap,
LINE352 pvVar7 = __p_buffer, dVar6 == 0) {
LINE353 dVar6 = __size_buffer;
LINE354 while (dVar6 = dVar6 - 1, __size_buffer - 3 <= dVar6) {
LINE355 *(undefined *)((int)__p_buffer + dVar6) = 0;
LINE356 }
LINE357 bVar12 = (byte)(((int)_size_from_colortype_withd - 1U) / _png_object->Width);
LINE358 if (bVar12 == 0) {
LINE359 bVar12 = 1;
LINE360 }
LINE361 local_1c3c = local_1c3c & 0xffffff00 | (uint)bVar12;
LINE362 png_process_colortype
LINE363 (local_1c04,local_1c10,__p_buffer,0,_size_from_colortype_withd,__size_buffer,
LINE364 local_1c3c,png_object_2,param_8,*(undefined4 *)(param_3 + 0x10));
LINE365 OS_memcpy(local_1c18,local_1c04,(size_t)_size_from_colortype_withd);
LINE366 OS_memcpy(local_1c04,local_1c10,(size_t)_size_from_colortype_withd);
LINE367 OS_memcpy(local_1c10,local_1c18,(size_t)_size_from_colortype_withd);
LINE368 dVar6 = FUN_101523a0(__higdibinfo,mys_table_function,(int)__p_buffer,_bits_qtity,
LINE369 __size_buffer);
LINE370 uVar13 = _kind_of_heap;
LINE371 pvVar7 = __p_buffer;
LINE372 if ((dVar6 != 0) || (_bits_qtity = _bits_qtity + 1, (int)_png_object->Height <= _bits_qtity)
LINE373 ) break;
LINE374 }
LINE375 }
LINE376 }
LINE377 if (local_1620 != 0) {
LINE378 uVar15 = *(uint *)(local_1620 + 0x18);
LINE379 kind_of_error_handle
LINE380 (uVar15,*(undefined4 *)(local_1620 + 0x14),
LINE381 "..\\..\\..\\..\\Common\\Formats\\pngread.c",0x96b);
LINE382 AF_memm_free_all(uVar15);
LINE383 }
LINE384 FUN_10105bf0(&local_1bf0);
LINE385 IO_byte_order_set(mys_table_function,1);
LINE386 kind_of_error_handle(uVar13,local_1c30,"..\\..\\..\\..\\Common\\Formats\\pngread.c",0xcfc);
LINE387 kind_of_error_handle(uVar13,local_1c04,"..\\..\\..\\..\\Common\\Formats\\pngread.c",0xcfd);
LINE388 kind_of_error_handle(uVar13,local_1c10,"..\\..\\..\\..\\Common\\Formats\\pngread.c",0xcfe);
LINE389 kind_of_error_handle(uVar13,pvVar7,"..\\..\\..\\..\\Common\\Formats\\pngread.c",0xcff);
LINE390 kind_of_error_handle(uVar13,local_1c18,"..\\..\\..\\..\\Common\\Formats\\pngread.c",0xd00);
LINE391 BVar4 = AF_error_check();
LINE392 if (BVar4 != False) {
LINE393 AF_err_record_get(0,(LPCHAR)(BVar4 + ~False),0,(LPINT)0x0,(LPAT_ERRCODE)0x0,&local_1c44,
LINE394 (LPAT_INT)0x0,(LPAT_INT)0x0,(LPCHAR)0x0,0);
LINE395 }
LINE396 BVar4 = @__security_check_cookie@4();
LINE397 return BVar4;
LINE398 }
The issue is happening at LINE355, where we can observe a while loop controlled by __size_buffer
.
The heap represented by the variable __p_buffer
is allocated at LINE130 and LINE128 through the use of the variable pvVar7
.
Its size is represented by the variable dVar6
, the same as __size_buffer
LINE127. It is computed at LINE126 and is the result of the raster_size_from_HIGDIBINFO
function call.
raster_size_from_HIGDIBINFO
pseudo-code is :
LINE400 dword raster_size_from_HIGDIBINFO(HIGDIBINFO HIGDIBINFO)
LINE401 {
LINE402 dword dVar1;
LINE403 uint uVar2;
LINE404
LINE405 dVar1 = IGDIBStd::DIB_bit_depth_get(HIGDIBINFO);
LINE406 if (dVar1 == 1) {
LINE407 uVar2 = DIB1bit_packed_raster_size_get(HIGDIBINFO);
LINE408 return uVar2;
LINE409 }
LINE410 if (dVar1 == 4) {
LINE411 dVar1 = DIB_width_get(HIGDIBINFO);
LINE412 return dVar1;
LINE413 }
LINE414 dVar1 = DIBStd_raster_size_get(HIGDIBINFO);
LINE415 return dVar1;
LINE416 }
LINE417
LINE418
LINE419 AT_DIMENSION DIB_width_get(HIGDIBINFO higdibinfo)
LINE420 {
LINE421 return higdibinfo->size_X;
LINE422 }
In our case, the DIB_width_get
is called by raster_size_from_HIGDIBINFO
to return the size computed and is directly read from the file into the PNG_CHUNK_IHDR
width value.
The vulnerability is happening with some PNG tags missing and with a width value equal to 3
.
In this case, the while loop is always true at LINE354.
We can confirm that while inspecting the memory, before the crash happens.
A correct heap metadata before entering into the while-loop:
0:000> dt _DPH_BLOCK_INFORMATION edx-0x20
ntdll!_DPH_BLOCK_INFORMATION
+0x000 StartStamp : 0xabcdbbbb
+0x004 Heap : 0x03001000 Void
+0x008 RequestedSize : 3
+0x00c ActualSize : 0x1000
+0x010 FreeQueue : _LIST_ENTRY [ 0x0 - 0x0 ]
+0x010 FreePushList : _SINGLE_LIST_ENTRY
+0x010 TraceIndex : 0
+0x018 StackTrace : 0x0447dcc4 Void
+0x01c EndStamp : 0xdcbabbbb
A bit further in time:
0:000> dt _DPH_BLOCK_INFORMATION edx-0x20
ntdll!_DPH_BLOCK_INFORMATION
+0x000 StartStamp : 0xabcdbbbb
+0x004 Heap : 0x03001000 Void
+0x008 RequestedSize : 3
+0x00c ActualSize : 0x1000
+0x010 FreeQueue : _LIST_ENTRY [ 0x0 - 0x0 ]
+0x010 FreePushList : _SINGLE_LIST_ENTRY
+0x010 TraceIndex : 0
+0x018 StackTrace : 0x0447dcc4 Void
+0x01c EndStamp : 0xbbbb
We can observe the EndStamp
starting to be overwritten. We can see this in happening in memory too:
0:000> db edx-32
0d03efc6 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0d03efd6 00 00 bb bb cd ab 00 10-00 03 03 00 00 00 00 10 ................
0d03efe6 00 00 00 00 00 00 00 00-00 00 c4 dc 47 04 bb bb ............G...
0d03eff6 ba dc c0 c0 c0 d0 d0 d0-d0 d0 ?? ?? ?? ?? ?? ?? ..........??????
Before the loop, you can see the edx
buffer containing the three bytes 0xC0
corresponding to our buffer. Then a bit later:
0:000> db edx-32
0d03efc6 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0d03efd6 00 00 bb bb cd ab 00 10-00 03 03 00 00 00 00 10 ................
0d03efe6 00 00 00 00 00 00 00 00-00 00 c4 dc 47 04 bb bb ............G...
0d03eff6 00 00 00 00 00 d0 d0 d0-d0 d0 ?? ?? ?? ?? ?? ?? ..........??????
This issue allows out-of-bounds write of a heap buffer, resulting in memory corruption.
0:000> !analyze -v
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************
KEY_VALUES_STRING: 1
Key : AV.Fault
Value: Write
Key : Analysis.CPU.mSec
Value: 2140
Key : Analysis.DebugAnalysisManager
Value: Create
Key : Analysis.Elapsed.mSec
Value: 12256
Key : Analysis.IO.Other.Mb
Value: 1
Key : Analysis.IO.Read.Mb
Value: 1
Key : Analysis.IO.Write.Mb
Value: 41
Key : Analysis.Init.CPU.mSec
Value: 19609
Key : Analysis.Init.Elapsed.mSec
Value: 537786
Key : Analysis.Memory.CommitPeak.Mb
Value: 176
Key : Timeline.OS.Boot.DeltaSec
Value: 17849
Key : WER.Process.Version
Value: 1.0.1.1
NTGLOBALFLAG: 2100000
APPLICATION_VERIFIER_FLAGS: 0
APPLICATION_VERIFIER_LOADED: 1
EXCEPTION_RECORD: (.exr -1)
ExceptionAddress: 75174029 (igCore20d!IG_mpi_page_set+0x000e7f09)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000001
Parameter[1]: 0d03dfff
Attempt to write to address 0d03dfff
FAULTING_THREAD: 00002398
PROCESS_NAME: Fuzzme.exe
WRITE_ADDRESS: 0d03dfff
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: 0d03dfff
STACK_TEXT:
WARNING: Stack unwind information not available. Following frames may be wrong.
0019f6f0 75174f14 0019fc3c 1000001e 0c410fe8 igCore20d!IG_mpi_page_set+0xe7f09
0019f724 75172632 0019fc3c 1000001e 0c410fe8 igCore20d!IG_mpi_page_set+0xe8df4
0019fbb4 750615b9 0019fc3c 0c410fe8 00000001 igCore20d!IG_mpi_page_set+0xe6512
0019fbec 750a08bc 00000000 0c410fe8 0019fc3c igCore20d!IG_image_savelist_get+0xb29
0019fe68 750a0239 00000000 05be6fe0 00000001 igCore20d!IG_mpi_page_set+0x1479c
0019fe88 75035bc7 00000000 05be6fe0 00000001 igCore20d!IG_mpi_page_set+0x14119
0019fea8 00402399 05be6fe0 0019febc 7699fb80 igCore20d!IG_load_file+0x47
0019fec0 004026c0 05be6fe0 0019fef8 05b4cf50 Fuzzme!fuzzme+0x19
0019ff28 00408407 00000005 05b46f98 05b4cf50 Fuzzme!fuzzme+0x340
0019ff70 769a00c9 003e1000 769a00b0 0019ffdc Fuzzme!fuzzme+0x6087
0019ff80 77777b4e 003e1000 5846efa4 00000000 KERNEL32!BaseThreadInitThunk+0x19
0019ffdc 77777b1e ffffffff 77798c55 00000000 ntdll!__RtlUserThreadStart+0x2f
0019ffec 00000000 0040848f 003e1000 00000000 ntdll!_RtlUserThreadStart+0x1b
STACK_COMMAND: ~0s ; .cxr ; kb
SYMBOL_NAME: igCore20d+e7f09
MODULE_NAME: igCore20d
IMAGE_NAME: igCore20d.dll
FAILURE_BUCKET_ID: INVALID_POINTER_WRITE_AVRF_c0000005_igCore20d.dll!Unknown
OSPLATFORM_TYPE: x86
OSNAME: Windows 8
IMAGE_VERSION: 20.1.0.117
FAILURE_ID_HASH: {d6c1493f-846f-f317-ed0f-78fca5c85e5d}
Followup: MachineOwner
---------
Release notes from the vendor can be found here:
https://help.accusoft.com/ImageGear/v20.3/Windows/DLL/webframe.html#release-notes.html
https://help.accusoft.com/ImageGear/v20.3/Linux/webframe.html#release-notes.html
2023-05-24 - Vendor Disclosure
2023-09-20 - Vendor Patch Release
2023-09-25 - Public Release
Discovered by Emmanuel Tacheau of Cisco Talos.