It's Bugs All the Way Down

Security Research by Dan Rosenberg

Breaking LibTIFF

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:

  • 1. It is a stack-based overflow. Many classic heap-based overflows are now difficult or impossible to exploit in practice, especially on Unix-based systems.
  • 2. The overflowed buffer is statically allocated as an integer array with two elements, which means that there are no stack cookies inserted into the vulnerable function in many cases. Most compilers only include stack canaries in functions with statically declared arrays of a certain minimum size, usually greater than the size of this array. For example, this setting can be controlled in gcc using the “ssp-buffer-size” setting.
  • 3. It is a memcpy() based overflow of arbitrary size, where the source is the attacker-controlled file. This means that null bytes are perfectly acceptable, and that an essentially arbitrary amount of the stack can be written by arbitrary data, with absolutely no character restrictions.
  • 4. Large portions of the TIFF file are mapped to reliable static addresses.

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.