Headline
CVE-2017-12099: TALOS-2017-0451 || Cisco Talos Intelligence Group
An exploitable integer overflow exists in the upgrade of the legacy Mesh attribute ‘tface’ 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.
Summary
An exploitable integer overflow exists in the upgrade of the legacy Mesh attribute tface 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:8334
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], there are a few fixes for versions before 2.42 [1]. One of which fixes specific CustomData for Mesh objects [2]
source/blender/blenloader/intern/versioning_legacy.c:2470
void blo_do_versions_pre250(FileData *fd, Library *lib, Main *main) {
...
if (main->versionfile <= 242) { [1]
...
for (me = main->mesh.first; me; me = me->id.next)
customdata_version_242(me); [2]
}
}
If the current Mesh object claims to have a number of total layers in its fdata and has the deprecated field tface, then a particular fix is applied.
source/blender/blenloader/intern/versioning_legacy.c:371
static void customdata_version_242(Mesh *me)
{
...
if (!me->fdata.totlayer) {
...
if (me->tface) {
...
me->mcol = CustomData_add_layer(&me->fdata, CD_MCOL, CD_CALLOC, NULL, me->totface); [3]
me->mtface = CustomData_add_layer(&me->fdata, CD_MTFACE, CD_CALLOC, NULL, me->totface);
Because tface is deprecated, the new mcol and mtface must be created. This is done via the CustomData_add_layer API. Note that me->totface is from the Mesh object created from file data [3]. CustomData_add_layer calls an internal API customData_add_layer__internal to allocate the memory necessary for the new mcol and mtface objects [4].
source/blender/blenkernel/intern/customdata.c:1930
void *CustomData_add_layer(CustomData *data, int type, int alloctype,
void *layerdata, int totelem)
{
CustomDataLayer *layer;
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
layer = customData_add_layer__internal(data, type, alloctype, layerdata,
totelem, typeInfo->defaultname); [4]
CustomData_update_typemap(data);
if (layer)
return layer->data;
return NULL;
}
During the creation of this customData layer, the size of the layer is calculated by multiplying the total elements (me->totface from above) by the size of the structure. By supplying a large enough value, this size variable can be overflown to be a much smaller number than required [5].
source/blender/blenkernel/intern/customdata.c:1841
static CustomDataLayer *customData_add_layer__internal(CustomData *data, int type, int alloctype, void *layerdata,
int totelem, const char *name)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
const size_t size = (size_t)totelem * typeInfo->size; [5]
...
else if (size > 0) {
if (alloctype == CD_DUPLICATE && layerdata) {
newlayerdata = MEM_mallocN(size, layerType_getName(type));
}
else {
newlayerdata = MEM_callocN(size, layerType_getName(type)); [6]
}
if (!newlayerdata)
return NULL;
}
...
data->layers[index].type = type;
data->layers[index].flag = flag;
data->layers[index].data = newlayerdata; [6]
...
return &data->layers[index]; [7]
This overflown size value is used in the MEM_callocN call [6]. This newly created allocation is then set in the layers.data element [7] and is then returned back to the call of customdata_version_242. This layer is then filled by leveraging old tface data [8].
source/blender/blenkernel/intern/customdata.c:1841
me->mcol = CustomData_add_layer(&me->fdata, CD_MCOL, CD_CALLOC, NULL, me->totface);
me->mtface = CustomData_add_layer(&me->fdata, CD_MTFACE, CD_CALLOC, NULL, me->totface);
mtf = me->mtface;
mcol = me->mcol;
tf = me->tface;
for (a = 0; a < me->totface; a++, mtf++, tf++, mcol += 4) {
memcpy(mcol, tf->col, sizeof(tf->col)); [8]
memcpy(mtf->uv, tf->uv, sizeof(tf->uv)); [8]
mtf->flag = tf->flag;
mtf->unwrap = tf->unwrap;
mtf->mode = tf->mode;
mtf->tile = tf->tile;
mtf->tpage = tf->tpage;
mtf->transp = tf->transp;
}
Because the allocated segments are too small to hold the requested data, the allocations are overflown causing a heap corruptions, potentially leading to code execution.
Crash Information
Initial allocation before overflow
eax=07a58acc ebx=07a58bb4 ecx=07a58acc edx=07a57448 esi=07aea40c edi=07a57444
eip=00a96cc0 esp=0022f918 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+0x148240:
00a96cc0 f30f6f4220 movdqu xmm0,xmmword ptr [edx+20h] ds:0023:07a57468=deadbeefdeadbeefdeadbeefdeadbeef
Return from customdata_add_layer with CD_MCOL
07948bb4 00000000 00000000 00000000 00000000 ................
07948bc4 abababab abababab feeefeee 00000000 ................
07948bd4 00000000 63385a14 0032ab6b 07948ac8 .....Z8ck.2.....
07948be4 07948cd0 feeefeee feeefeee feeefeee ................
07948bf4 feeefeee feeefeee feeefeee feeefeee ................
07948c04 feeefeee 773b5a03 1c32ab6b 00000068 .....Z;wk.2.h...
07948c14 07948cfc 07948b2c 07d52104 07d5b814 ....,....!......
07948c24 00000000 00000000 6873654d 00000000 ........Mesh....
address 07948bb4 found in
_HEAP @ 3ee0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
07948ba8 0006 0000 [00] 07948bb0 00014 - (busy)
State of the allocation after partial overflow
Breakpoint 2 hit
eax=00000003 ebx=07a58be4 ecx=07a58b50 edx=07a574fc esi=07aea40c edi=07a574f8
eip=00a96cc0 esp=0022f918 ebp=0022f928 iopl=0 nv up ei ng nz na po cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000283
blender!PyInit_mathutils_noise_types+0x148240:
00a96cc0 f30f6f4220 movdqu xmm0,xmmword ptr [edx+20h] ds:0023:07a5751c=deadbeefdeadbeefdeadbeefdeadbeef
0:000> dc 07a58bb4
07a58bb4 deadbeef deadbeef deadbeef deadbeef ................
07a58bc4 deadbeef deadbeef deadbeef deadbeef ................
07a58bd4 deadbeef deadbeef deadbeef deadbeef ................
07a58be4 07a58cd0 feeefeee feeefeee feeefeee ................
07a58bf4 feeefeee feeefeee feeefeee feeefeee ................
07a58c04 feeefeee 153a09d0 1c325688 00000068 ......:..V2.h...
07a58c14 07a58cfc 07a58b2c 07e61fcc 07e6b6e4 ....,...........
07a58c24 00000000 00000000 6873654d 00000000 ........Mesh....
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-27 - Vendor Disclosure
2018-01-11 - Public Release
Discovered by Cory Duplantis and a member of Cisco Talos.