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.
ACDSystems Canvas Draw 4.0.0
8.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CWE-122: Heap-Based Buffer Overflow
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.
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.
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
Discovered by Tyler Bohan of Cisco Talos.