Talos Vulnerability Report

TALOS-2016-0171

Apple Image I/O API Tiled TIFF Remote Code Execution Vulnerability

July 18, 2016

CVE Number

CVE-2016-4631

SUMMARY

An exploitable heap based buffer overflow exists in the handling of TIFF images on Apple OS X and iOS operating systems. A crafted TIFF document can lead to a heap based buffer overflow resulting in remote code execution. This vulnerability can be triggered via malicious web page, MMS message, iMessage or a file attachment delivered by other means when opened in applications using the Apple Image I/O API.

TESTED VERSIONS

OSX El Capitan - 10.11.4
iOS - 9.3.1

PRODUCT URLs

https://developer.apple.com/osx/download/

CVSSv3 SCORE

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

DETAILS

This vulnerability is present in the Apple Image IO API which is used for all image handling on OS X including rendering images in Preview and Safari.

There exists a vulnerability in the parsing and handling of Tiled TIFF images. A specially crafted TIFF image file can lead to an out of bounds write and ultimately to remote code execution.

TIFF (Tagged Image File Format) images consist of data describing the image in the header then tags throughout the image file describing how and where the data should be displayed. Running the attached test case through a Tiff analyzer shows us the following output:

Here we see a crash on an attempt to write to memory at the address held in RAX. Investigating further we see RAX is pointing to the very end of a heap block yet there is still a lot of data to be written as RDX (counter) is still 0xFE. It is also noted that the malloced buffer is the same size as the tile width which becomes of interest when looking further into what is causing this. Decompiling the code near where the malloc block is allocated in ImageIO`copyImageBlockSetTiledTIFF is shown below:

The information is read in from the tiff image then some calculations are performed on it. At [1] it compares the tile width and the image width and defaults to the smaller value. It does the same check for image length at [2]. The tile width and length, 255 and 1024, are both longer than the image width and length, 2 and 1, thus the code defaults to the smaller values. It then calculates the needed size by multiplying the calculated width from above, times the number of samples[4]. If this is less than the size of a tile, one tile size is allocated, 255, which is what happens in this case[5]. Further into this function we see where our overwrite is happening:

The code is reading in tile data from the tiff image, one tile per sample, and moving the buffer forward the size of one tile[1]. The problem comes in when we look at the previous code and see it defaulted to the smaller width and only allocated 255 bytes, enough for one sample. Every subsequent sample writes out of bounds and further corrupts memory. The number of samples is easily controlled in the tiff image allowing for further heap corruption quite easily.