CVE-2017-12081
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.
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.
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.
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....>...
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
2017-09-06 - Vendor Disclosure
2018-01-11 - Public Release
Discovered by Cory Duplantis and a member of Cisco Talos.