Talos Vulnerability Report

TALOS-2018-0541

ACD Systems Canvas Draw 4 setRasterData Heap Overflow Code Execution Vulnerability

July 19, 2018
CVE Number

CVE-2018-3857

Summary

An exploitable heap overflow exists in the TIFF parsing functionality of Canvas Draw version 4.0.0. A specially crafted TIFF image processed via the application can lead to an out-of-bounds write, overwriting arbitrary data. An attacker can deliver a TIFF image to trigger this vulnerability and gain code execution.

Tested Versions

ACDSystems Canvas Draw 4.0.0

Product URLs

https://www.canvasgfx.com/en/products/canvas-draw

CVSSv3 Score

8.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

CWE

CWE-122: Heap-Based Buffer Overflow

Details

Canvas Draw 4 is a graphics editing tool used to create and edit images, as well as other graphic-related material. This product has a large user base, and is popular in its specific field. The vulnerable component is in the handling of TIFF images. TIFF is a raster-based image format used in graphics editing projects, thus making it a very common file format for such an application.

The vulnerability arises in the parsing of a compressed and tiled TIFF image. TIFF has support for multiple versions of image compression, and an image application is expected to be able to handle them. The tag used to define levels of compression is tag number 259. The compression used in the vulnerable TIFF image is CCITT Group 3. A brief description from the TIFF specification is shown below.

 Each strip  must begin on  a byte  boundary.   (But recall  that an image can be a
 single strip.)   Rows  that are  not the first row of a strip are
 not required  to begin on a byte boundary.  The data is stored as
 bytes,  not words - byte-reversal  is   not  allowed.

The crash happens due to an invalid object being freed on the free list. This is often caused by a heap overflow, and subsequently corruption of the free list. Investigating further, we can confirm our suspicions. Employing Guard Malloc, the crash happens inside of memcpy and the stack trace is below.

frame #0: 0x00007fffc7946ec9 libsystem_platform.dylib`_platform_memmove$VARIANT$Haswell + 41
frame #1: 0x000000016d9a85fa ImageIO`CIGReadFile_CB_ext::setRasterData(unsigned char*, long long, unsigned int) + 340
frame #2: 0x0000000101e03f41 ImageGear18`LoadG3_1D_ProGold + 985
frame #3: 0x0000000101f82b14 ImageGear18`_TIF_Proc_G3_Comp + 653
frame #4: 0x0000000101f7bfa6 ImageGear18`_TIF_read + 451

The overflow variable for the vulnerable function call is calculated via a series of shifts and multiplication, similar to that of decompressing data. The data it is decompressing, however, is user-controlled, and it is not verified before use. The calculation is shown below.

memcpy_size = mem_3 * ((mem_2 * ((*(v4 + 5) >> 32) - mem_1)) / 8);

Then the use of the variable:

memcpy(*(*(v4 + 4) + 1064LL), buf_1, memcpy_size);

Using a debugger, we can verify this issue and see the actual size calculations and the overflow:

    <+321>:  mov    rdi, qword ptr [rax + 0x428]
    <+328>:  movsxd rdx, dword ptr [rbp - 0x2c]
    <+332>:  mov    rsi, r15
->  <+335>:  call   0x16b3ed3ea               ; symbol stub for: memcpy

Above is the vulnerable call to memcpy with the destination buffer in RDI, source in RSI and size in RBX. Inspecting their sizes, we see:

RDI = ALLOC 0x45e671ef0-0x45e672ff7 [size=4360]
RSI = ALLOC 0x45e6c2ed0-0x45e6c3fff [size=4400]
RDX = 0x150f20 (1380128)

There is a large overflow happening here copying over the bounds of the buffer supplied in RDI and writing to arbitrary memory. We can also see the crash is caused by the out-of-bounds write:

* thread #1: tid = 0x6718b, 0x00007fffc7946ec9 libsystem_platform.dylib`_platform_memmove$VARIANT$Haswell + 41, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x45e673000)
frame #0: 0x00007fffc7946ec9 libsystem_platform.dylib`_platform_memmove$VARIANT$Haswell + 41
libsystem_platform.dylib`_platform_memmove$VARIANT$Haswell:
    0x7fffc7946ec5 <+37>:  mov    rcx, rdx
    0x7fffc7946ec8 <+40>:  cld    
->  0x7fffc7946ec9 <+41>:  rep   

The rep command is used to repeat a previous operation, moving RDX to RCX and writing out of bounds. This overwrites parts of the free list, causing a vulnerable condition to arise later in the program when this data is then attempted to be freed. An attacker could craft arbitrary data to overwrite with and gain code execution through this vulnerability.

Crash Information

Crashed thread log = 
: Dispatch queue: com.apple.main-thread
0   libsystem_platform.dylib        0x00007fffc7946ec9 _platform_memmove$VARIANT$Haswell + 41
1   com.acdsystem.canvastool.ImageIO    0x0000000163ebe5fa CIGReadFile_CB_ext::setRasterData(unsigned char*, long long, unsigned int) + 340
2   ImageGear18                     0x0000000103349f41 LoadG3_1D_ProGold + 985
3   ImageGear18                     0x00000001034c8b14 _TIF_Proc_G3_Comp + 653
4   ImageGear18                     0x00000001034c1fa6 _TIF_read + 451
5   ImageGear18                     0x00000001034c1d85 TIF_read + 261
6   ImageGear18                     0x00000001033bedfd GPb_fltrm_READ_call_param + 178
7   ImageGear18                     0x00000001033bed45 GPb_fltrm_READ_call + 21
8   ImageGear18                     0x0000000103395bbf iIG_load_FD_CB_ex + 411
9   ImageGear18                     0x00000001035073b6 IG_load_FD_CB_ex + 91
10  com.acdsystem.canvastool.ImageIO    0x0000000163ebdd12 CIGReadFile_CB_ext::readFile() + 836
11  com.acdsystem.canvastool.ImageIO    0x0000000163eea633 ImageGearAcquireProc(short, AcquireRecord*, int*, short*) + 722
12  com.acdsystem.canvastool.ImageIO    0x0000000163eeabf2 ImageIORunAcquireProc(_ImageIOAcquireState*) + 750
13  com.acdsystem.canvastool.ImageIO    0x0000000163ee878a 0x163e6a000 + 518026
14  com.acdsystem.canvastool.ImageIO    0x0000000163ee9ef4 DoImportFile(ImportFileMsg*) + 817
15  com.acdsystem.canvastool.ImageIO    0x0000000163e9d7c1 toolmain() + 917
16  com.acdsystem.canvastool.ImageIO    0x0000000163ec990a stdtool(TToolCallBlock*) + 122
17  com.acdsystem.canvastool.ImageIO    0x0000000163ec9889 cvtool_main(TToolCallBlock*) + 9
18  com.acdsystems.Canvas-Draw4     0x00000001017015b0 0x1015c8000 + 1283504
19  com.acdsystems.Canvas-Draw4     0x00000001021d6b76 0x1015c8000 + 12643190
20  com.acdsystems.Canvas-Draw4     0x00000001021d6438 0x1015c8000 + 12641336
21  com.acdsystems.Canvas-Draw4     0x00000001023068a7 0x1015c8000 + 13887655
22  com.apple.AppKit                0x00007fffafee4bd3 -[NSApplication _doOpenFile:ok:tryTemp:] + 322
23  com.apple.AppKit                0x00007fffafaa3ba7 -[NSApplication finishLaunching] + 1624
24  com.apple.AppKit                0x00007fffafaa3148 -[NSApplication run] + 267
25  com.apple.AppKit                0x00007fffafa6de0e NSApplicationMain + 1237
26  libdyld.dylib                   0x00007fffc7734235 start + 1

log name is: ./crashlogs/f.crashlog.txt
---
exception=EXC_BAD_ACCESS:signal=11:is_exploitable=yes:instruction_disassembly=rep/movsb (%esi),(%edi):instruction_address=0x00007fffc7946ec9:access_type=write:access_address=0x00000003dabc7000:
Crash accessing invalid address.

Timeline

2018-03-20 - Vendor Disclosure
2018-04-18 - 30 day follow up
2018-04-19 - Vendor escalated to Canvas development team
2018-05-02 - 45 day follow up
2018-06-25 - Vendor confirmed fix scheduled for next update
2018-07-19 - Public Release

Credit

Discovered by Tyler Bohan of Cisco Talos.