Talos Vulnerability Report

TALOS-2017-0406

Blender Sequencer imb_loadtiff Integer Overflow Code Execution Vulnerability

January 11, 2018
CVE Number

CVE-2017-2899

Summary

An exploitable integer overflow exists in the TIFF loading functionality of the Blender open-source 3d creation suite version 2.78c. A specially crafted .tif file can cause an integer overflow resulting in a buffer overflow which can allow for code execution under the context of the application. An attacker can convince a user to use the file as an asset via the sequencer in order to trigger this vulnerability.

Tested Versions

Blender v2.78c

Product URLs

http://www.blender.org git://git.blender.org/blender.git

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-190 - Integer Overflow or Wraparound

Details

Blender is a professional, open-source 3d computer graphics application. It is used for creating animated films, visual effects, art, 3d printed applications, and video games. It is also capable of doing minimalistic video editing and sequencing as needed by the user. There are various features that it provides which allow for a user to perform a multitude of actions as required by a particular project.

This vulnerability exists with how the Blender application loads a TIFF file as a resource for the video sequencer. When allocating space for the image data within a .tif file, the application will perform some arithmetic which can overflow. This result will then be used to perform an allocation which can allow for an undersized buffer. Later when the application attempts to render the image data into this buffer, a heap-based buffer overflow will occur.

When loading an image file, the function IMB_loadiffname in the source/blender/imbuf/intern/readimage.c file will be called. Inside this function, the application will first open the file and then call the IMB_loadifffile function [2].

source/blender/imbuf/intern/readimage.c:212
ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[IM_MAX_SPACE])
{
...
    file = BLI_open(filepath_tx, O_BINARY | O_RDONLY, 0);                   // [1]
    if (file == -1)
        return NULL;

    ibuf = IMB_loadifffile(file, filepath, flags, colorspace, filepath_tx); // [2]

Inside the IMB_loadifffile function, the application will first map the whole file into memory using the mmap system-call [3]. After the file is successfully mapped into memory, the resulting pages will be passed to the IMB_ibImageFromMemory function [4]. This function is responsible for figuring out which file-format handlers to use, and then to call its respective loader.

source/blender/imbuf/intern/readimage.c:165
ImBuf *IMB_loadifffile(int file, const char *filepath, int flags, char colorspace[IM_MAX_SPACE], const char *descr)
{
...
    imb_mmap_lock();
    mem = mmap(NULL, size, PROT_READ, MAP_SHARED, file, 0);             // [3]
    imb_mmap_unlock();

    if (mem == (unsigned char *) -1) {
        fprintf(stderr, "%s: couldn't get mapping %s\n", __func__, descr);
        return NULL;
    }

    ibuf = IMB_ibImageFromMemory(mem, size, flags, colorspace, descr);  // [4]

Inside the following function, the application will iterate through a global list that contains different handlers for all of the image files that the application supports. At [5], the application will call the function responsible for loading the image out of memory.

source/blender/imbuf/intern/readimage.c:104
ImBuf *IMB_ibImageFromMemory(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE], const char *descr)
{
...
    for (type = IMB_FILE_TYPES; type < IMB_FILE_TYPES_LAST; type++) {
        if (type->load) {
            ibuf = type->load(mem, size, flags, effective_colorspace);              // [5]
            if (ibuf) {
                imb_handle_alpha(ibuf, flags, colorspace, effective_colorspace);
                return ibuf;
            }
        }
    }

Once the application checks the magic and determines that the file to be loaded is of the TIFF format, the imb_loadtiff function will be dispatched to. At [6], the application will utilize the libTIFF library to open the file. After doing this successfully, the dimensions for the image will be fetched from the tags within the TIFF image’s directory. The SAMPLESPERPIXEL field will be used to determine the number of bits for each decoded pixel [7]. Afterwards these values will be combined in order to allocate space for the buffer to decode the image into [8]. This structure is later copied. If the product of the width, height, and ib_depth variables are larger than 32-bits then the image buffer will be undersized when trying to decode into it. At [9], the application will call the img_read_tiff_pixels function decode the TIFF image with.

source/blender/imbuf/intern/tiff.c:523
ImBuf *imb_loadtiff(const unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
...
    image = imb_tiff_client_open(&memFile, mem, size);                          // [6]

    if (image == NULL) {
        printf("imb_loadtiff: could not open TIFF IO layer.\n");
        return NULL;
    }

    /* allocate the image buffer */
    TIFFGetField(image, TIFFTAG_IMAGEWIDTH,  &width);
    TIFFGetField(image, TIFFTAG_IMAGELENGTH, &height);
    TIFFGetField(image, TIFFTAG_SAMPLESPERPIXEL, &spp);
    
    ib_depth = (spp == 3) ? 24 : 32;                                            // [7]
    
    ibuf = IMB_allocImBuf(width, height, ib_depth, 0);                          // [8]
...
    /* read pixels */
    if (!(ibuf->flags & IB_tilecache) && !imb_read_tiff_pixels(ibuf, image)) {  // [9]
        fprintf(stderr, "imb_loadtiff: Failed to read tiff image.\n");
        TIFFClose(image);
        return NULL;
    }

When actually decoding the TIFF file format, the application will read a couple more fields from the TIFF file’s directory [10]. These fields determine which decoder to choose from. Afterwards, the application will make a copy of the image buffer that was allocated at [8]. This is done by grabbing the original values and multiplying them. Due to the application not checking to see if these values can be larger than 32-bits, this will result another undersized buffer. Depending on the value of PLANARCONFIG or the BITSPERSAMPLE tag, one of the pieces of code at [12] will be used to decode the image. Due to the undersized buffer as a result of an integer overflow, this can cause a heap-based buffer overflow which can lead to code execution under the context of the application.

source/blender/imbuf/intern/tiff.c:377
static int imb_read_tiff_pixels(ImBuf *ibuf, TIFF *image)
{
...

    TIFFGetField(image, TIFFTAG_BITSPERSAMPLE, &bitspersample);                     // [9]
    TIFFGetField(image, TIFFTAG_SAMPLESPERPIXEL, &spp);     /* number of 'channels' */
    TIFFGetField(image, TIFFTAG_PLANARCONFIG, &config);
...
    tmpibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, ib_flag);              // [10]
    
    /* simple RGBA image */
    if (!(bitspersample == 32 || bitspersample == 16)) {
        success |= TIFFReadRGBAImage(image, ibuf->x, ibuf->y, tmpibuf->rect, 0);    // [11]
    }
    /* contiguous channels: RGBRGBRGB */
    else if (config == PLANARCONFIG_CONTIG) {
        for (row = 0; row < ibuf->y; row++) {
            int ib_offset = ibuf->x * ibuf->y * 4 - ibuf->x * 4 * (row + 1);
        
            if (bitspersample == 32) {
                success |= TIFFReadScanline(image, fbuf, row, 0);                   // [11]
                scanline_contig_32bit(tmpibuf->rect_float + ib_offset, fbuf, ibuf->x, spp);
                
            }
            else if (bitspersample == 16) {
                success |= TIFFReadScanline(image, sbuf, row, 0);                   // [11]
                scanline_contig_16bit(tmpibuf->rect_float + ib_offset, sbuf, ibuf->x, spp);
            }
        }
        /* separate channels: RRRGGGBBB */
    }

Crash Information

(14d0.2428): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=141b7004 ebx=00010000 ecx=00000000 edx=00000000 esi=00a40004 edi=00000004
eip=02177110 esp=0030e858 ebp=0030e868 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
blender!xmlCheckHTTPInput+0x434a0:
02177110 8916            mov     dword ptr [esi],edx  ds:002b:00a40004=00000003

Exploit Proof-of-Concept

Included with this advisory is a generator for the vulnerability. This proof-of-concept requires python and takes a single-argument which is the filename to write the .tif file to.

$ python poc.py $FILENAME.tif

To trigger the vulnerability, one can simply add it as an asset or they can pass it as an argument to the blender executable.

$ /path/to/blender.exe -a $FILENAME.tif

Mitigation

In order to mitigate this vulnerability, it is recommended to not use untrusted image files as an asset when using the sequencer.

Timeline

2017-09-06 - Vendor Disclosure
2018-01-11 - Public Release

Credit

Discovered by a member of Cisco Talos.