CVE-2017-2899
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.
Blender v2.78c
http://www.blender.org git://git.blender.org/blender.git
8.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CWE-190 - Integer Overflow or Wraparound
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 */
}
(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
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
In order to mitigate this vulnerability, it is recommended to not use untrusted image files as an asset when using the sequencer.
2017-09-06 - Vendor Disclosure
2018-01-11 - Public Release
Discovered by a member of Cisco Talos.