Talos Vulnerability Report
LibTIFF PixarLogDecode Remote Code Execution Vulnerability
October 25, 2016
An exploitable heap based buffer overflow exists in the handling of compressed TIFF images in LibTIFF’s PixarLogDecode api. A crafted TIFF document can lead to a heap based buffer overflow resulting in remote code execution. The vulnerability can be triggered through any user controlled TIFF that is handled by this functionality.
LibTiff - 4.0.6
8.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
This vulnerability is present in the PixarLogDecode function inside of LibTIFF. This vulnerability is present whenever attempting to decompress a PixarLog compressed TIFF image.
To decompress the PixarLog compressed data inside of a TIFF image, LibTIFF uses the Zlib compression library. First the parameters needed to pass to Zlib are set up with a function call to PixarLogSetupDecode. A buffer is allocated with the code shown below:
tbuf_size = multiply_ms(multiply_ms(multiply_ms(sp->stride, td->td_imagewidth), td->td_rowsperstrip), sizeof(uint16)); /* add one more stride in case input ends mid-stride */ tbuf_size = add_ms(tbuf_size, sizeof(uint16) * sp->stride); if (tbuf_size == 0) return (0); /* TODO: this is an error return without error report through TIFFErrorExt */ sp->tbuf = (uint16 *) _TIFFmalloc(tbuf_size);
Later this buffer is used when calling the Zlib library function inflate which is responsible for the decompression.
sp->stream.next_out = (unsigned char *) sp->tbuf; sp->stream.avail_out = (uInt) (nsamples * sizeof(uint16));
Now seeing how these parameters are used in the inflate function:
Output space is provided to deflate() by setting avail_out to the number of available output bytes and next_out to a pointer to that space. strm.avail_out = CHUNK; strm.next_out = out;
So seeing this we see that this is used by Zlib as a space to write output too. The problem arises with how LibTIFF calculates out these two values. First the buffer is calculated above with this line of code:
tbuf_size = multiply_ms(multiply_ms(multiply_ms(sp->stride, td->td_imagewidth), td->td_rowsperstrip), sizeof(uint16));
Which comes out significantly less than the avail_out value calculated below:
sp->stream.avail_out = (uInt) (nsamples * sizeof(uint16));
Passing this undersized buffer into the Zlib inflate function causes a heap overflow that could be potentially leveraged into remote code execution.
Crashed thread log = : Dispatch queue: com.apple.main-thread 0 libsystem_platform.dylib 0x00007fff8b1b303b _platform_memmove$VARIANT$Haswell + 283 1 libz.1.dylib 0x00007fff9563d945 inflate + 2424 2 libtiff.5.dylib 0x000000010922235d PixarLogDecode + 301 (tif_pixarlog.c:795) 3 libtiff.5.dylib 0x00000001092294af TIFFReadEncodedTile + 191 (tif_read.c:668) 4 libtiff.5.dylib 0x000000010920a0a9 gtTileContig + 745 (tif_getimage.c:656) 5 libtiff.5.dylib 0x0000000109209b55 TIFFReadRGBATile + 469 (tif_getimage.c:495) 6 a.out 0x00000001091dbf46 main + 86 7 libdyld.dylib 0x00007fff85a3e5ad start + 1 log name is: ./crashlogs/4.crashlog.txt --- exception=EXC_BAD_ACCESS:signal=11:is_exploitable=yes:instruction_disassembly=.byte 0xc5 #bad opcode:instruction_address=0x00007fff8b1b303b:access_type=unknown:access_address=0x000000010985b000: Crash accessing invalid address. Consider running it again with libgmalloc(3) to see if the log changes.
Discovered by Tyler Bohan of Cisco Talos
2016-09-20 - Vendor Disclosure
2016-10-25 - Public Release