Talos Vulnerability Report

TALOS-2016-0190

LibTIFF Tag Extension Remote Code Execution Vulnerability

October 25, 2016

Report ID

CVE-2016-8331

Summary

An exploitable remote code execution vulnerability exists in the handling of TIFF images in LibTIFF. A crafted TIFF document can lead to a type confusion vulnerability resulting in remote code execution. This vulnerability can be triggered via a TIFF file delivered to the application using LibTIFF’s tag extension functionality.

Tested Versions

LibTIFF - 4.0.6

Product URLs

http://www.remotesensing.org/libtiff/

CVSSv3 Score

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

Details

There exists a vulnerability in the parsing and handling of TIFF images. A specially crafted TIFF image file can lead to an out of bounds write and ultimately to remote code execution. This vulnerability is present in the LibTIFF api and is present in the standard build.

TIFF offers support for tag extensions allowing for more tags than the standard TIFF specification. One such tag is number 326, BadFaxLines. When using this tag in LibTIFF it is possible to have a type confusion vulnerability where LibTIFF attempts to read a mistyped argument off of the variable argument list. Running the provided trigger through the Thumbnail utility provided by LibTIFF gives us the following crash.

   1130               case TIFF_DOUBLE:
-> 1131                 *va_arg(ap, double*) =
   1132                   *(double *)val;

(lldb) h
-=[registers]=-
[rax: 0x00000001003002d0] [rbx: 0x0000000000000146] [rcx: 0x0000390000003a03]
[rdx: 0x0000000000000020] [rsi: 0x0000000000000018] [rdi: 0x0000000100300170]
[rsp: 0x00007fff5fbff8e0] [rbp: 0x00007fff5fbff910] [ pc: 0x000000010001e08b]
[ r8: 0x0000000000000040] [ r9: 0x00007fff71d71110] [r10: 0xffffffffffffffff]
[r11: 0x0000000000000246] [r12: 0x0000000000000000] [r13: 0x0000000100300110]
[r14: 0x00007fff5fbff9d0] [r15: 0x0000000000000000] [efl: 0x0000000000010202]
[rflags: 00000000 NZ NS NO NC ND NI]

-=[stack]=-
7fff5fbff8e0 | 0000000000000001 0000000101002c00 | .........,......
7fff5fbff8f0 | eb008c3017da5776 0000000101002c00 | vW..0....,......
7fff5fbff900 | 0000000000000146 0000000000000000 | F...............
7fff5fbff910 | 00007fff5fbffa10 0000000100011338 | ..._....8.......

-=[disassembly]=-
    0x10001e080 <+9504>:  lea    rsp, [rsp + 0x98]
    0x10001e088 <+9512>:  mov    rcx, qword ptr [rcx]
->  0x10001e08b <+9515>:  mov    qword ptr [rcx], rax
    0x10001e08e <+9518>:  jmp    0x10001e30e               ; <+10158> at tif_dir.c:855
    0x10001e093 <+9523>:  nop    word ptr cs:[rax + rax]
    0x10001e0a0 <+9536>:  lea    rsp, [rsp - 0x98]

(lldb) bt
* thread #1: tid = 0x3008d362, 0x000000010001e08b libtiff.5.dylib`_TIFFVGetField(tif=<unavailable>, tag=326, ap=<unavailable>) + 9515 at tif_dir.c:1131, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x390000003a03)
  * frame #0: 0x000000010001e08b libtiff.5.dylib`_TIFFVGetField(tif=<unavailable>, tag=326, ap=<unavailable>) + 9515 at tif_dir.c:1131
    frame #1: 0x0000000100011338 libtiff.5.dylib`TIFFGetField [inlined] TIFFVGetField(tif=0x0000000101002c00, tag=326) + 335 at tif_dir.c:1176
    frame #2: 0x00000001000111e9 libtiff.5.dylib`TIFFGetField(tif=0x0000000101002c00, tag=326) + 345 at tif_dir.c:1160
    frame #3: 0x0000000100001c7b thumbnail`main + 12 at thumbnail.c:178
    frame #4: 0x0000000100001c6f thumbnail`main [inlined] cpTags at thumbnail.c:310
    frame #5: 0x0000000100001c6f thumbnail`main [inlined] cpIFD at thumbnail.c:386
    frame #6: 0x0000000100001c6f thumbnail`main(argc=<unavailable>, argv=<unavailable>) + 2415 at thumbnail.c:133
    frame #7: 0x00007fff8260f5ad libdyld.dylib`start + 1
    frame #8: 0x00007fff8260f5ad libdyld.dylib`start + 1

The crash occurs at the following lines of code:

tif_dir.c
   1130               case TIFF_DOUBLE:
-> 1131                 *va_arg(ap, double*) =
   1132                   *(double *)val;

Recall that va_arg retrieves the next available argument from the argument list ap. Let’s take a look at where this function was called.

int
TIFFGetField(TIFF * tif, uint32 tag, ...)
{
    int status;
    va_list ap;

    va_start(ap, tag)
    status = TIFFVGetField(tif, tag, ap); // Crash happens here
    va_end(ap);
    return (status);
}

We see that we are passing in the variable argument list from TIFFGetField into TIFFVGetField. Effectively this is a wrapper for creating the variable list that will be handled by each individual tag. Let’s traverse up one more function to see how TIFFGetField is called.

tools/thumbnail.c

136 #define	CopyField(tag, v) \
137     if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v)

165 case TIFF_LONG:
166		{ uint32 longv;
167		  CopyField(tag, longv); // Call to TIFFGetField
168		}
169		break;

Analyzing the crash a bit we can see the tag that we are crashing on is indeed number 326. Looking into the source we can see a double value is expected on the argument list but instead there is nothing there.

tif_dir.c:599
    switch (fip->field_type) {
            ...
tif_dir.c:659
        case TIFF_DOUBLE:
            *va_arg(ap, double*) =
                *(double *)val;
            ret_val = 1;
            break;
        default:
            ret_val = 0;
            break;
    }

The vulnerability arises due to the field type being determined to be a double, yet the variable argument list is empty causing an out of bounds write and ultimately leading to an exploitable condition.

Crash Information

Crashed thread log =
: Dispatch queue: com.apple.main-thread
0   libtiff.5.dylib                 0x00000001045ea5e0 _TIFFVGetField + 2784 (tif_dir.c:1132)
1   libtiff.5.dylib                 0x00000001045e774b TIFFGetField + 219 (tif_dir.c:1177)
2   thumbnail                       0x00000001045d8c7b main + 2427 (thumbnail.c:178)
3   libdyld.dylib                   0x00007fff8260f5ad start + 1

log name is: ./crashlogs/libtiff-thumbnail-report_badfaxlines_tif.crashlog.txt
---
exception=EXC_BAD_ACCESS:signal=11:is_exploitable=yes:instruction_disassembly=movq  %rax,(%rcx):instruction_address=0x00000001045ea5e0:access_type=write:access_address=0x0000430000004403:
Crash accessing invalid address.  Consider running it again with libgmalloc(3) to see if the log changes.

Credit

Tyler Bohan
Cory Duplantis

Timeline

2016-07-25 - Vendor Disclosure
2016-10-25 - Public Release