Talos Vulnerability Report

TALOS-2017-0433

Blender vcol_to_fcol Integer Overflow Code Execution Vulnerability

January 11, 2018
CVE Number

CVE-2017-12081

Summary

An exploitable integer overflow exists in the upgrade of a legacy Mesh attribute of the Blender open-source 3d creation suite v2.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 open the file or use it as a library 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.

During the initial load of a .blend file, a version check of the file is triggered in order to adjust legacy features. Blender has a series of fixes isolated by blocks based off of the version, shown below.

source/blender/blenloader/intern/readfile.c
static void do_versions(FileData *fd, Library *lib, Main *main)
{
    blo_do_versions_pre250(fd, lib, main); // [0]
    blo_do_versions_250(fd, lib, main);
    blo_do_versions_260(fd, lib, main);
    blo_do_versions_270(fd, lib, main);
}

The numbers 250 - 270 correspond to checks pertaining to that particular version block. During the checks for version before 2.5 [0], a transformation of the mcol within Mesh objects occurs.

source/blender/blenloader/intern/versioning_legacy.c:584
void blo_do_versions_pre250(FileData *fd, Library *lib, Main *main) {
    ...
    if (main->versionfile <= 106) {
        /* mcol changed */
        Mesh *me = main->mesh.first;
        while (me) {
            if (me->mcol) [1]
                vcol_to_fcol(me);
            me = me->id.next;
        }

    } 
}

If the current .blend file contains a Mesh object that also has a valid mcol [1], then a call to vcol_to_fcol is issued in order to upgrade from the old mcol array to the newer version.

The Mesh struct is shown below with a few highlighted attributes.

source/blender/makesdna/DNA_mesh_types.h:55
typedef struct Mesh {
    ...
    struct MFace *mface;
    ...
    struct MCol *mcol;
    ...
    int totvert, totedge, totface, totselect;
    ...
} Mesh

Three key attributes of note: mface, mcol, and totvert. mface is an array of object mode faces for tessellation, mcol is an array of colors to color those faces, and totface is the total number of faces. The code that handles the transformation of old mcol to new mcol is shown below.

source/blender/blenloader/intern/versioning_legacy.c
static void vcol_to_fcol(Mesh *me)
{
    MFace *mface;
    unsigned int *mcol, *mcoln, *mcolmain;
    int a;

    if (me->totface == 0 || me->mcol == NULL)
        return;

    mcoln = mcolmain = MEM_mallocN(4*sizeof(int)*me->totface, "mcoln"); [1]
    mcol = (unsigned int *)me->mcol;
    mface = me->mface;
    for (a = me->totface; a > 0; a--, mface++) { [2]
        mcoln[0] = mcol[mface->v1];
        mcoln[1] = mcol[mface->v2];
        mcoln[2] = mcol[mface->v3];
        mcoln[3] = mcol[mface->v4];
        mcoln += 4;
    }

    MEM_freeN(me->mcol);
    me->mcol = (MCol *)mcolmain;
}

The new mcol array (mcolmain) is first allocated using using the totface from the Mesh object read from the file [1]. The loop immediately after the allocation is then used to populate the newly created array [2]. Each entry into the new mcolmain array is 4 integers. In order to calculate the total size of the array, the total number of faces is multipled by the total size of each entry. When provided with a large number of total faces, this multiplication can result in an integer overflow, causing a smaller than necessary array to be created. The loop then iterates over the large number of faces, overflowing the heap allocation, potentially causing code execution.

Crash Information

Initial allocation of 0x10 bytes

eax=00000010 ebx=00000000 ecx=00000001 edx=00000003 esi=07bcaac4 edi=07bca544
eip=00a974f9 esp=0022f914 ebp=0022f928 iopl=0         ov up ei pl nz na po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000a03
blender!PyInit_mathutils_noise_types+0x148a79:
00a974f9 ff157c485803    call    dword ptr [blender!NvOptimusEnablement+0x9b4 (0358487c)] ds:0023:0358487c=00efd330

Initial state of the allocation

eax=07f01874 ebx=00000000 ecx=03aec61c edx=01018d74 esi=07b1aabc edi=07b1a53c
eip=00a974ff esp=0022f914 ebp=0022f928 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
blender!PyInit_mathutils_noise_types+0x148a7f:
00a974ff 8b9e90040000    mov     ebx,dword ptr [esi+490h] ds:0023:07b1af4c=70000001
0:000> dc 0x7f01874
07f01874  00000000 00000000 00000000 00000000  ................
07f01884  00000000 79016259 80000000 00000132  ....Yb.y....2...
07f01894  00000000 00000000 00000000 00000000  ................
07f018a4  00000000 7901625d 80000000 00000136  ....]b.y....6...
07f018b4  00000000 00000000 00000000 00000000  ................
07f018c4  00000000 79016251 80000000 0000013a  ....Qb.y....:...
07f018d4  00000000 00000000 00000000 00000000  ................
07f018e4  00000000 79016255 80000000 0000013e  ....Ub.y....>...

State of the allocation after overflow

eax=61616161 ebx=6ffffffb ecx=07f018dc edx=0bc434d4 esi=07b1aabc edi=0bbb01f4
eip=00a9754a esp=0022f91c ebp=0022f928 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
blender!PyInit_mathutils_noise_types+0x148aca:
00a9754a 85db            test    ebx,ebx
0:000> dc 0x7f01874
07f01874  61616161 61616161 61616161 61616161  aaaaaaaaaaaaaaaa
07f01884  61616161 61616161 61616161 61616161  aaaaaaaaaaaaaaaa
07f01894  61616161 61616161 61616161 61616161  aaaaaaaaaaaaaaaa
07f018a4  61616161 61616161 61616161 61616161  aaaaaaaaaaaaaaaa
07f018b4  61616161 61616161 61616161 61616161  aaaaaaaaaaaaaaaa
07f018c4  61616161 61616161 61616161 61616161  aaaaaaaaaaaaaaaa
07f018d4  00000000 00000000 00000000 00000000  ................
07f018e4  00000000 79016255 80000000 0000013e  ....Ub.y....>...

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 with the provided proof-of-concept, run blender with the newly created proof-of-concept.

$ /path/to/blender.exe $FILENAME.blend

Timeline

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

Credit

Discovered by Cory Duplantis and a member of Cisco Talos.