Talos Vulnerability Report

TALOS-2020-0990

Accusoft ImageGear JPEG SOFx Code Execution Vulnerability

February 10, 2020
CVE Number

CVE-2020-6066

An exploitable out-of-bounds write vulnerability exists in the igcore19d.dll JPEG SOFx parser of the Accusoft ImageGear 19.5.0 library. A specially crafted JPEG 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

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

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, DICOM, PDF, Microsoft Office and others. There is a vulnerability in the JPEG raster image parser. A specially crafted JPEG file can lead to an out-of-bounds write resulting in remote code execution.

If we try to load a malformed JPEG 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=00000000 ebx=00000000 ecx=0bae1000 edx=00000000 esi=00000000 edi=0bae0ffe
eip=5b9c2181 esp=012ff514 ebp=012ff65c iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
igCore19d!IG_mpi_page_set+0xb6df1:
5b9c2181 66890471        mov     word ptr [ecx+esi*2],ax  ds:002b:0bae1000=????

The calculated address 0x0bae1000 points to the page with the PAGE_GUARD flag causing an access violation. Checking attributes related with the buffer we can see :

0:000> !heap -p -a 0bae1000
    address 0bae1000 found in
    _DPH_HEAP_ROOT @ 5c21000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                 be8164c:          badf000             2000 -          bade000             4000
    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
    5b9c59aa igCore19d!IG_mpi_page_set+0x000ba61a
    5b9bef63 igCore19d!IG_mpi_page_set+0x000b3bd3
    5b9d6115 igCore19d!IG_mpi_page_set+0x000cad85
    5b9d605c igCore19d!IG_mpi_page_set+0x000caccc
    5b9d40b1 igCore19d!IG_mpi_page_set+0x000c8d21
    5b9d57da igCore19d!IG_mpi_page_set+0x000ca44a
    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

that an attempt of write operation was performed just after the available buffer memory range. Further analysis revealed that:

the buffer is allocated based on values : (SOFx->X_image * 2) * (SOFx->COMPS[1]->compNr +1)
SOFx->X_image          : offset -> 0xA5
SOFx->COMPS[1]->compNr : offset -> 0xAD

Operations related with the above buffer are made inside a while loop which looks as follows:

Line 1  int __stdcall sub_5DD71490(int *a1, _DWORD *a2, __int16 a3, int a4, int a5, int (__stdcall *a6)(signed int, int, int *, _DWORD *))
Line 2  {
Line 3 
Line 4      while ( !a3 )
Line 5      {
Line 6  LABEL_112:
Line 7        v63 = 0;
Line 8        v142 = 0;
Line 9        if ( __nr_comp <= 0 )
Line 10         goto LABEL_214;
Line 11       __v64 = (struct_v64 *)v152;
Line 12       v65 = 0;
Line 13       do
Line 14       {
Line 15         v66 = __v64->someArray;
Line 16         i__ = 80 * v65;
Line 17         loop_index = 0;
Line 18         v137 = 80 * v65;
Line 19         if ( *(_DWORD *)(80 * v65 + v66 + 28) <= 0 )
Line 20           goto LABEL_212;
Line 21         v68 = v65;
Line 22         v124 = v68 * 4;
Line 23         v69 = v166[v65];
Line 24         v70 = v164[v68] - v69;
Line 25         v127__ = v69;
Line 26         inside_loop_index = loop_index;
Line 27         value_var = v70;
Line 28         do
Line 29         {
Line 30           baseAddr = __v64->someArray;
Line 31           value_ecx = v127__ + inside_loop_index;
Line 32           value_esi = *(_DWORD *)(baseAddr + i__ + 44);
Line 33           someStruct = (struct_v75 *)(i__ + baseAddr);
Line 34           basePtr = someStruct->basePtr;
Line 35           v77 = someStruct->dword18 == 0;
Line 36           v78 = someStruct->dword18 < 0;
Line 37           _buffer = &basePtr[value_esi * (value_ecx + value_var)];
Line 38           v80 = &basePtr[value_ecx * value_esi];
Line 39           i__ = v137;
Line 40           buffer = _buffer;
Line 41           v144 = v80;
Line 42           v159 = 0;
Line 43           if ( !v78 && !v77 )
Line 44           {
Line 45             v155 = _buffer - 1;
Line 46             while ( 1 )
Line 47             {
Line 48               if ( v158 < 16 )
Line 49               {
Line 50   
Line 51 LABEL_196:
Line 52               if ( *v152 <= 8 )
Line 53                 buffer[index] = (unsigned __int8)(v109 + ((_BYTE)v99 << v131));
Line 54               else
Line 55                 buffer[index] = v109 + ((_WORD)v99 << v131);
Line 56                 }
Line 57 LABEL_210:
Line 58           someArray = __v64->someArray;
Line 59           inside_loop_index = loop_index + 1;
Line 60           loop_index = inside_loop_index;
Line 61         }
Line 62         while ( inside_loop_index < *(_DWORD *)(i__ + someArray + 28) );
Line 63         __nr_comp = nr_comp;
Line 64         v63 = (int)v142;
Line 65 LABEL_212:
Line 66         ++v63;
Line 67         *(_DWORD *)(__v64->someArray + i__ + 32) += 2 * *(_DWORD *)(__v64->someArray + i__ + 24);
Line 68         v65 = (signed __int16)v63;
Line 69         v142 = (_DWORD *)v63;
Line 70       }
Line 71       while ( (signed __int16)v63 < __nr_comp );
Line 72

Important for us is while loop line 62, which is controlled by :

    (SOFx->COMPS[1]->Vert & 0xF)
     offset - > 0xAC

Each loop cycle the buffer base address is increased by :

    SOFx->X_image

which in our case equals 0x1000. We can quickly estimate that after second iteration an out-of-bounds write operation will appear in line 53.

As we can see an attacker controls all presented variables just by proper file content manipulation. Increasing the loop count via the SOFx->COMPS[1]->Vert variable allows an attacker to cause an out-of-bounds write leading to memory corruption which can result in remote code execution.

Crash Information

(7588.5f40): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=0bd0d000 edx=00000000 esi=00000000 edi=0bd0cffe
eip=5b9c2181 esp=00afeed0 ebp=00aff018 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
igCore19d!IG_mpi_page_set+0xb6df1:
5b9c2181 66890471        mov     word ptr [ecx+esi*2],ax  ds:002b:0bd0d000=????
0:000> kb
 # ChildEBP RetAddr  Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
00 00aff018 5b9bf1e6 09864f60 00aff044 00000001 igCore19d!IG_mpi_page_set+0xb6df1
01 00aff0a4 5b9d6115 00000003 5b9d2100 0b708720 igCore19d!IG_mpi_page_set+0xb3e56
02 00aff0c0 5b9d605c 0b708720 09864f60 0000ffda igCore19d!IG_mpi_page_set+0xcad85
03 00aff0e4 5b9d40b1 0b708720 09864f60 00aff10c igCore19d!IG_mpi_page_set+0xcaccc
04 00aff104 5b9d57da 00afffc3 1000001b 0987cf70 igCore19d!IG_mpi_page_set+0xc8d21
05 00aff144 5b8e07c9 1000001b 0987cf70 00000001 igCore19d!IG_mpi_page_set+0xca44a
06 00aff17c 5b91fb97 00000000 0987cf70 00aff1cc igCore19d!IG_image_savelist_get+0xb29
07 00aff3f8 5b91f4f9 00000000 09ed1fb8 00000001 igCore19d!IG_mpi_page_set+0x14807
08 00aff418 5b8b6007 00000000 09ed1fb8 00000001 igCore19d!IG_mpi_page_set+0x14169
09 00aff438 00ef59ac 09ed1fb8 00aff524 00aff548 igCore19d!IG_load_file+0x47
0a 00aff538 00ef61a7 09ed1fb8 00aff66c 00000021 simple_exe_141+0x159ac
0b 00aff704 00ef6cbe 00000004 09e7ef90 09d59f20 simple_exe_141+0x161a7
0c 00aff718 00ef6b27 c72c7e22 00ef15e1 00ef15e1 simple_exe_141+0x16cbe
0d 00aff774 00ef69bd 00aff784 00ef6d38 00aff794 simple_exe_141+0x16b27
0e 00aff77c 00ef6d38 00aff794 74f56359 008e4000 simple_exe_141+0x169bd
0f 00aff784 74f56359 008e4000 74f56340 00aff7f0 simple_exe_141+0x16d38
10 00aff794 772f7b74 008e4000 0ea51c40 00000000 KERNEL32!BaseThreadInitThunk+0x19
11 00aff7f0 772f7b44 ffffffff 77318f0c 00000000 ntdll!__RtlUserThreadStart+0x2f
12 00aff800 00000000 00ef15e1 008e4000 00000000 ntdll!_RtlUserThreadStart+0x1b
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:     19.5.0.0
    Product version:  19.5.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.5.0.0
        FileVersion:      19.5.0.0
        FileDescription:  Accusoft ImageGear CORE DLL 
        LegalCopyright:   Copyright 1996-2019 Accusoft Corporation. All rights reserved.
        LegalTrademarks:  ImageGearÆ and AccusoftÆ are registered trademarks of Accusoft Corporation

Timeline

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

Credit

Discovered by Emmanuel Tacheau and a member of Cisco Talos.