Talos Vulnerability Report

TALOS-2017-0377

Gdk-Pixbuf TIFF tiff_image_parse Code Execution Vulnerability

August 30, 2017
CVE Number

CVE-2017-2870

Summary

An exploitable integer overflow vulnerability exists in the tiffimageparse functionality of Gdk-Pixbuf 2.36.6 when compiled with Clang. A specially crafted tiff file can cause a heap-overflow resulting in remote code execution. An attacker can send a file or a URL to trigger this vulnerability.

Tested Versions

Gdk-Pixbuf 2.36.6 commit: aba8d88798dfc2f3856ea0ddda14b06174bbb2bc compiled with clang -O3 flag libtiff 4.0.6

Product URLs

https://developer.gnome.org/gdk-pixbuf/

CVSSv3 Score

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

CWE

CWE-758: Reliance on Undefined, Unspecified, or Implementation-Defined Behavior

Details

Gdk-Pixbuf is a toolkit for image loading and pixel buffer manipulation used in various type of desktop applications: image viewers(GNOME thumbnailer), web browsers (Chromium, Firefox), media players (VLC), etc.

The vulnerability exists in the TIFF parser and only manifests itself when the library is compiled with high optimization flags -O3 (tested with Clang, gcc does not remove the check). Several defined if statements inside the tiff_image_parse function are responsible of integer overflow checks or at least that was their intention. Because the checks are made on signed integers, the condition cannot evaluate to false unless an integer overflow occurs. According to the C standard, a signed integer overflow is defined as "Undefined Bahavior", thus behaviour related to it is implementation dependent and in the case of Clang the check is removed. Finally the lack of proper integer overflows check leads to heap overflow and can allow attackers to obtain arbitrary code execution.

The code below is removed from compilation process because it would be true if a signed integer overflow would occur which is undefined bahavior:

Line 89         gint width, height, rowstride, bytes;
Line 128        rowstride = width * 4;
Line 129        if (rowstride / 4 != width) { /* overflow */
Line 130                g_set_error_literal (error,
Line 131                                     GDK_PIXBUF_ERROR,
Line 132                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Line 133                                     _("Dimensions of TIFF image too large"));
Line 134                return NULL;                
Line 135        }
Line 136        
Line 137        bytes = height * rowstride;
Line 138        if (bytes / rowstride != height) { /* overflow */
Line 139                g_set_error_literal (error,
Line 140                                     GDK_PIXBUF_ERROR,
Line 141                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Line 142                                     _("Dimensions of TIFF image too large"));
Line 143                return NULL;                
Line 144        }

in our case the variables have the following values:

width  = 0x8020
height = 0xfff7

which causes an integer overflow at line 137:

bytes = height * ( width * 4)

Then, based on the overflowed value, a buffer is allocated :

Line 160       pixels = g_try_malloc (bytes);

Then all three parameters: width,height and the allocated bytes buffer are passed as arguments:

Line 272        if (!TIFFReadRGBAImageOriented (tiff, width, height, (uint32 *)pixels, ORIENTATION_TOPLEFT, 1)) {   

Because buffer bytes was allocated based on overflowed value, width and height parameters mismatch the size of the buffer which leads to out of bound writes (Line 1362) inside the put1bitbwtile function while reading RGB values:

libtiff/tif_getimage.c:1326
Line 1351   /*
Line 1352    * 1-bit bilevel => colormap/RGB
Line 1353    */
Line 1354   DECLAREContigPutFunc(put1bitbwtile)
Line 1355   {
Line 1356       uint32** BWmap = img->BWmap;
Line 1357
Line 1358       (void) x; (void) y;
Line 1359       fromskew /= 8;
Line 1360       while (h-- > 0) {
Line 1361       uint32* bw;
Line 1362       UNROLL8(w, bw = BWmap[*pp++], *cp++ = *bw++);
Line 1363       cp += toskew;
Line 1364       pp += fromskew;
Line 1365       }
Line 1366   }   

Crash Information

valgrind ./pixbuf-read crashes/tiff_bug.tiff
==32378== Invalid write of size 4
==32378==    at 0x4B5D71D: put1bitbwtile (tif_getimage.c:1326)
==32378==    by 0x4B5BE5E: gtTileContig (tif_getimage.c:673)
==32378==    by 0x4B5B810: TIFFRGBAImageGet (tif_getimage.c:495)
==32378==    by 0x4B5B8ED: TIFFReadRGBAImageOriented (tif_getimage.c:514)
==32378==    by 0x4067A59: tiff_image_parse (io-tiff.c:272)
==32378==    by 0x406634C: gdk_pixbuf__tiff_image_stop_load (io-tiff.c:477)
==32378==    by 0x4048442: gdk_pixbuf_loader_close (gdk-pixbuf-loader.c:822)
==32378==    by 0x8048903: test_loader (pixbuf-read.c:35)
==32378==    by 0x8048903: main (pixbuf-read.c:75)
==32378==  Address 0x5343ba8 is 0 bytes after a block of size 7,207,808 alloc'd
==32378==    at 0x402D17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==32378==    by 0x432C3BF: g_try_malloc (in /lib/i386-linux-gnu/libglib-2.0.so.0.4800.2)
==32378==    by 0x4067584: tiff_image_parse (io-tiff.c:160)
==32378==    by 0x406634C: gdk_pixbuf__tiff_image_stop_load (io-tiff.c:477)
==32378==    by 0x4048442: gdk_pixbuf_loader_close (gdk-pixbuf-loader.c:822)
==32378==    by 0x8048903: test_loader (pixbuf-read.c:35)
==32378==    by 0x8048903: main (pixbuf-read.c:75)

Timeline

2017-07-13 - Vendor Disclosure
2017-08-30 - Public Release

Credit

Discovered by Marcin 'Icewall' Noga of Cisco Talos.