Talos Vulnerability Report

TALOS-2017-0415

Blender Directory Browsing Thumbnail Viewer Integer Overflow Code Execution Vulnerability

January 11, 2018
CVE Number

CVE-2017-2908

Summary

An exploitable integer overflow exists in the thumbnail functionality of the Blender open-source 3d creation suite version 2.78c. A specially crafted .blend 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 render the thumbnail for the file while in the File->Open dialog.

Tested Versions

Blender v2.78c

Product URLs

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

CVSSv3 Score

7.5 - CVSS:3.0/AV:N/AC:H/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 the thumbnail for a .blend file when browsing directory contents. When allocating space for the thumbnail image within a .blend 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 copy the thumbnail data into this buffer, a heap-based buffer overflow will occur.

When loading a thumbnail from a .blend file, the BLO_thumbnail_from_file is called. Near the beginning of this function, the application will call the blo_openblenderfile_minimal function to read the header from the file [1]. The header from the file contains a magic value which determines the pointer-size and byte order used within the file. Immediately afterwards at [2], the application will call the read_file_thumbnail function. This function will search for the block-header matching the value "TEST" [3]. After this is determined, the application will validate the length of the block. During this process, the application will read two signed 32-bit numbers. Afterwards, the application will validate that the block's length is smaller than the result of the BLEN_THUMB_MEMSIZE_FILE macro [4]. This macro will multiply the two numbers together, add the constant 2, and then multiply them by the size of an int. Due to the application failing to check that the product of these values can result in more than 32-bits, this test can overflow thus bypassing the validation.

source/blender/blenloader/intern/readfile.c:1401
BlendThumbnail *BLO_thumbnail_from_file(const char *filepath)

    FileData *fd;
    BlendThumbnail *data;
    int *fd_data;

    fd = blo_openblenderfile_minimal(filepath);                                 // [1]
    fd_data = fd ? read_file_thumbnail(fd) : NULL;                              // [2] \

    if (fd_data) {
        const size_t sz = BLEN_THUMB_MEMSIZE(fd_data[0], fd_data[1]);
        data = MEM_mallocN(sz, __func__);

        BLI_assert((sz - sizeof(*data)) == (BLEN_THUMB_MEMSIZE_FILE(fd_data[0], fd_data[1]) - (sizeof(*fd_data) * 2)));
        data->width = fd_data[0];
        data->height = fd_data[1];
        memcpy(data->rect, &fd_data[2], sz - sizeof(*data));

\
source/blender/blenloader/intern/readfile.c:946
static int *read_file_thumbnail(FileData *fd)

    BHead *bhead;
    int *blend_thumb = NULL;

    for (bhead = blo_firstbhead(fd); bhead; bhead = blo_nextbhead(fd, bhead)) {
        if (bhead->code == TEST) {                                              // [3]
            const bool do_endian_swap = (fd->flags & FD_FLAGS_SWITCH_ENDIAN) != 0;
            int *data = (int *)(bhead + 1);

            if (bhead->len < (2 * sizeof(int))) {
                break;


            if (do_endian_swap) {
                BLI_endian_switch_int32(&data[0]);
                BLI_endian_switch_int32(&data[1]);


            if (bhead->len < BLEN_THUMB_MEMSIZE_FILE(data[0], data[1])) {       // [4] \
                break;


            blend_thumb = data;
            break;

...
    return blend_thumb;

source/blender/blenloader/BLO_blend_defs.h:78
#define BLEN_THUMB_MEMSIZE_FILE(_x, _y) (sizeof(int) * (size_t)(2 + (_x) * (_y)))

Once validating the thumbnail header and then returning a pointer to the thumbnail data, the application will resume execution of the BLO_thumbnail_from_file function. Using the data returned from read_file_thumbnail, the application will then pass both of the signed 32-bit numbers to the BLEN_THUMB_MEMSIZE macro [5]. This macro will multiply the two numbers together along with the size of an int. Afterwards, the size of a BlendThumbnail structure will be added to the result. Due to the application not checking that this result may be larger than 32-bits, an integer overflow may occur. Once this overflown size is calculated, an allocation will be made which may be undersized due to this vulnerability. The overflow is then triggered by the assignments to data->width, data->height, or the memcpy operation that happens at [6].

source/blender/blenloader/intern/readfile.c:1401
BlendThumbnail *BLO_thumbnail_from_file(const char *filepath)

    FileData *fd;
    BlendThumbnail *data;
    int *fd_data;

    fd = blo_openblenderfile_minimal(filepath);
    fd_data = fd ? read_file_thumbnail(fd) : NULL;

    if (fd_data) {
        const size_t sz = BLEN_THUMB_MEMSIZE(fd_data[0], fd_data[1]);       // [5] \
        data = MEM_mallocN(sz, __func__);

        BLI_assert((sz - sizeof(*data)) == (BLEN_THUMB_MEMSIZE_FILE(fd_data[0], fd_data[1]) - (sizeof(*fd_data) * 2)));
        data->width = fd_data[0];
        data->height = fd_data[1];
        memcpy(data->rect, &fd_data[2], sz - sizeof(*data));                // [6]

\
source/blender/blenkernel/BKE_main.h:125
#define BLEN_THUMB_MEMSIZE(_x, _y) (sizeof(BlendThumbnail) + (size_t)((_x) * (_y)) * sizeof(int))

Crash Information

(20.2d90): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=3295affc ebx=336b2ff8 ecx=0b6a51a1 edx=010186b0 esi=00000000 edi=32b4ab3c
eip=0081c8be esp=2e56ca98 ebp=2e56cab0 iopl=0         nv up ei pl nz ac pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010216
blender!PyInit_mathutils_noise_types+0x12de3e:
0081c8be 894804          mov     dword ptr [eax+4],ecx ds:002b:3295b000=????????
0:016> !heap -p -a @eax
    address 3295affc found in
    _DPH_HEAP_ROOT @ 8811000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                32990ed4:         3295aff8                4 -         3295a000             2000

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 .blend file to.

$ python poc.py $FILENAME.blend

To trigger the vulnerability, one can simply browse to the same directory as the file via the File->Open dialog and then view thumbnails by enabling it on the toolbar.

Mitigation

In order to mitigate this vulnerability, it is recommended to not use thumbnails when browsing a directory of projects.

Timeline

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

Credit

Discovered by a member of Cisco Talos.