Talos Vulnerability Report

TALOS-2017-0411

Blender Sequencer imb_loadhdr Integer Overflow Code Execution Vulnerability

January 11, 2018
CVE Number

CVE-2017-2904

Summary

An exploitable integer overflow exists in the RADIANCE loading functionality of the Blender open-source 3d creation suite version 2.78c. A specially crafted .hdr 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 RADIANCE file as a resource for the video sequencer. When allocating space for the image data within a .hdr 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;
            }
        }
    }

A RADIANCE image file is prefixed with the “#?” characters. Once this is determined, the application will execute the imb_loadhdr function. At [6], the application will verify that the fingerprint matches the “#?” characters. After this is done, the application will scan over the length of the file looking for an empty line of white space [7]. As described in the comments, the resolution immediately follows. At [8], the application will read the dimensions for the image.

source/blender/imbuf/intern/radiance_hdr.c:210
struct ImBuf *imb_loadhdr(const unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
...
    if (imb_is_a_hdr((void *)mem)) {                            // [6]
...
        /* find empty line, next line is resolution info */
        for (x = 1; x < size; x++) {                            // [7]
            if ((mem[x - 1] == '\n') && (mem[x] == '\n')) {
                found = 1;
                break;
            }
        }
        if (found && (x < (size + 2))) {
            if (sscanf((char *)&mem[x + 1], "%79s %d %79s %d", (char *)&oriY, &height,
                       (char *)&oriX, &width) != 4)             // [8]
            {
                return NULL;
            }

Once the dimensions for the image are read, the application can then allocate a buffer to decode the image into. At [9], the application will take the product of the width and height that was read earlier and multiply them by the number of bytes-per-pixel which is specified as 32-bits. If the product is larger than 32-bits, then this image buffer can be made to overflow resulting in an undersized buffer.

source/blender/imbuf/intern/radiance_hdr.c:243
            if (flags & IB_test) ibuf = IMB_allocImBuf(width, height, 32, 0);       // [9]
            else ibuf = IMB_allocImBuf(width, height, 32, (flags & IB_rect) | IB_rectfloat);

            if (UNLIKELY(ibuf == NULL)) {
                return NULL;
            }
            ibuf->ftype = IMB_FTYPE_RADHDR;

            if (flags & IB_alphamode_detect)
                ibuf->flags |= IB_alphamode_premul;
...

Afterwards, the application will begin to read data from the file and use them to write into this buffer [10]. This can lead to a heap-based buffer overflow which can lead to code execution under the context of the application. There is also another vulnerability that may occur due to the application explicitly trusting the width. At [11], the application will take the width and multiply it by the size of the sline variable which is 4 bytes. Due to the width being a signed 32-bit value read from the file using sscanf("%d"), an aggressor can specify any size that they wish. If the product of the width and the integer 4 is larger than 32-bits, then this can also trigger a undersized allocation leading to a buffer overflow. Later when the application reads data from the file into the sline variable, another buffer overflow will occur [12].

source/blender/imbuf/intern/radiance_hdr.c:258
            /* read in and decode the actual data */
            sline = (RGBE *)MEM_mallocN(sizeof(*sline) * width, __func__);          // [11]
            rect_float = ibuf->rect_float;
            
            for (y = 0; y < height; y++) {
                ptr = freadcolrs(sline, ptr, width, mem_eof);                       // [12]
                if (ptr == NULL) {
                    printf("WARNING! HDR decode error, image may be just truncated, or completely wrong...\n");
                    break;
                }
                for (x = 0; x < width; x++) {                                       // [10]
                    /* convert to ldr */
                    RGBE2FLOAT(sline[x], fcol);
                    *rect_float++ = fcol[RED];
                    *rect_float++ = fcol[GRN];
                    *rect_float++ = fcol[BLU];
                    *rect_float++ = 1.0f;
                }
            }
            MEM_freeN(sline);

Crash Information

(2c80.332c): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=031afa9c ebx=00000000 ecx=0e9aaff8 edx=0e9aaff8 esi=10410ff4 edi=00010000 eip=0205827f esp=0050eb9c ebp=0050eba0 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202 blender!PyInit_aud+0x519eaf: 0205827f f30f10460c movss xmm0,dword ptr [esi+0Ch] ds:002b:10411000=???????? 0:000> u . blender!PyInit_aud+0x519eaf: 0205827f f30f10460c movss xmm0,dword ptr [esi+0Ch] 02058284 0f2ec1 ucomiss xmm0,xmm1 02058287 f30f11450c movss dword ptr [ebp+0Ch],xmm0

Exploit Proof-of-Concept

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 poc.hdr

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-17 - Vendor Disclosure
2018-01-11 - Public Release

Credit

Discovered by a member of Cisco Talos.