Talos Vulnerability Report

TALOS-2023-1749

Accusoft ImageGear create_png_object heap-based buffer overflow vulnerability

September 25, 2023
CVE Number

CVE-2023-32614

SUMMARY

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.

CONFIRMED VULNERABLE VERSIONS

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

PRODUCT URLS

ImageGear - https://www.accusoft.com/products/imagegear-collection/

CVSSv3 SCORE

7.0 - CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:L

CWE

CWE-124 - Buffer Underwrite (‘Buffer Underflow’)

DETAILS

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.

Crash Information

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
---------
VENDOR RESPONSE

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

TIMELINE

2023-05-24 - Vendor Disclosure
2023-09-20 - Vendor Patch Release
2023-09-25 - Public Release

Credit

Discovered by Emmanuel Tacheau of Cisco Talos.