In this post I’ll be providing details on a vulnerability I discovered in libtiff, which was recently made public with CVE-2010-2067. libtiff is linked into a wide range of applications, including browsers, document viewers, file browsers, and image software, and it might even be possible to leverage this bug to achieve code execution on embedded devices.
The vulnerability only affects libtiff version 3.9.2, which was the stable release of libtiff from November 2009 until this past week. The impact is arbitrary code execution when processing a maliciously crafted TIFF image using any software linked against libtiff. Code execution bugs in image libraries are serious to begin with, but there are several factors that make this bug especially exploitable:
Put together, these factors allow for reasonably robust exploits. For my proof-of-concept, I developed an exploit for the tiffinfo tool provided with libtiff that works on Linux systems with ASLR and no NX support, but depending on the application that libtiff is linked against, I would expect it to be possible to write a reliable ROP exploit that works on platforms with NX/DEP.
The vulnerability is in libtiff/tif_dirread.c, which handles reading and processing TIFF directory entries. TIFF directory entries are stored in a table in the TIFF header, and provide indexing and type information used when processing TIFF metadata. The TIFFDirEntry
struct is declared in libtiff/tiff.h:
typedef struct { uint16 tdir_tag; /* tag */ uint16 tdir_type; /* data type */ uint32 tdir_count; /* number of items; length in spec */ uint32 tdir_offset; /* byte offset to field data */ } TIFFDirEntry;
The vulnerability is reachable via the TIFFReadCustomDirectory()
function, which is invoked by TIFFReadEXIFDirectory()
. This function is used to sequentially read and process tags containing EXIF metadata in a TIFF image file. In the TIFFReadCustomDirectory()
function, we notice a special case for a certain tag type:
/* * EXIF tags which need to be specifically processed. */ switch (dp->tdir_tag) { case EXIFTAG_SUBJECTDISTANCE: (void) TIFFFetchSubjectDistance(tif, dp); break;
The TIFFFetchSubjectDistance()
function looks like this:
static int TIFFFetchSubjectDistance(TIFF* tif, TIFFDirEntry* dir) { uint32 l[2]; float v; int ok = 0; if (TIFFFetchData(tif, dir, (char *)l) && cvtRational(tif, dir, l[0], l[1], &v)) { /* * XXX: Numerator 0xFFFFFFFF means that we have infinite * distance. Indicate that with a negative floating point * SubjectDistance value. */ ok = TIFFSetField(tif, dir->tdir_tag, (l[0] != 0xFFFFFFFF) ? v : -v); } return ok; }
The l[]
array is allocated on the stack, and populated using TIFFFetchData()
, passing the current TIFFDirEntry
struct as an argument. Here’s where the problem is: the TIFFFetchData()
function uses the tdir_count
member of the TIFFDirEntry
struct to determine the amount of data to be copied into its argument:
static tsize_t TIFFFetchData(TIFF* tif, TIFFDirEntry* dir, char* cp) { uint32 w = TIFFDataWidth((TIFFDataType) dir->tdir_type); uint32 cc = dir->tdir_count * w; /* Various checks for integer overflows * in the above calculation */ ... _TIFFmemcpy(cp, tif->tif_base + dir->tdir_offset, cc);
The TIFFFetchData()
function expects this count to have been validated to prevent overflows before being called, but as we can see above, no such validation takes place for this particular tag. To exploit this, all we need to do is create an EXIF directory entry with a tdir_tag
of EXIFTAG_SUBJECTDISTANCE
(defined as 37382 = 0x9206), the appropriate matching tdir_type
for this tag (TIFF_RATIONAL
, defined as 0x5), and a tdir_count
value large enough to overflow this buffer and overwrite the saved return address on the stack. The tdir_offset
member should point to a location in the TIFF file where we expect data to be copied from onto the stack.
I experienced one minor hassle during exploit development for this bug. The current TIFF struct is dereferenced several times before returning from the vulnerable function, so if you overflow the stack without considering this, an out-of-bounds read will likely take place and the function will never return. Fortunately, in my testing this TIFF struct was always mapped to a static address, so it was trivial to replace appropriate bytes in the TIFF file such that the overflow does not alter the address of this struct, and execution will continue as normal.
This vulnerability might look familiar to those who remember Tavis Ormandy’s CVE-2006-3459. That’s because the code path is nearly identical – in Tavis’s vulnerability, a lack of validation on the tdir_count
value in TIFFFetchShortPair()
led to a similar stack overflow.
This entry was posted on Tuesday, June 29th, 2010 at 9:00 am and is filed under Exploitation. You can follow any responses to this entry through the RSS 2.0 feed. Both comments and pings are currently closed.