Talos Vulnerability Report

TALOS-2016-0205

LibTIFF PixarLogDecode Remote Code Execution Vulnerability

October 25, 2016
CVE Number

CVE-2016-5875

Summary

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.

Tested Versions

LibTiff - 4.0.6

Product URLs

http://libtiff.maptools.org

CVSSv3 Score

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

Details

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.

Crash Information

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.

Timeline

2016-09-20 - Vendor Disclosure
2016-10-25 - Public Release

Credit

Discovered by Tyler Bohan of Cisco Talos.