|
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Core Security Technologies - CoreLabs Advisory
http://www.coresecurity.com/corelabs/
Google SketchUp 'lib3ds' 3DS Importer Memory Corruption
1. *Advisory Information*
Title: Google SketchUp 'lib3ds' 3DS Importer Memory Corruption
Advisory Id: CORE-2009-1209
Advisory URL:
http://www.coresecurity.com/content/google-sketchup-vulnerability
Date published: 2010-01-13
Date of last update: 2010-01-12
Vendors contacted: Google
Release mode: Coordinated release
2. *Vulnerability Information*
Class: Failure to Constrain Operations within the Bounds of a Memory
Buffer [CWE-119], Out-of-bounds Write [CWE-787]
Impact: Code execution
Remotely Exploitable: Yes (client-side)
Locally Exploitable: No
Bugtraq ID: 37708
CVE Name: CVE-2010-0280
3. *Vulnerability Description*
Google SketchUp is a 3D modeling program designed for architects, civil
engineers, filmmakers, game developers, and related professions. Google
SketchUp bundles an old version of 'lib3ds', a library used to process
3DS files. This library is being compiled in a way that leads to
improper validation of data when importing 3DS files; this condition can
be exploited by remote attackers to trigger a memory corruption
vulnerability by enticing an unsuspecting user to open a specially
crafted 3DS file, possibly leading to arbitrary code execution.
4. *Vulnerable packages*
. Google SketchUp 7.0.10247
. Google SketchUp 7.1.4871
. Google SketchUp 7.1.6087
. Older versions are probably affected too, but they were not checked.
5. *Non-vulnerable packages*
. Google SketchUp 7.1.6860 (Windows)
. Google SketchUp 7.6859 (MAC OS X)
6. *Vendor Information, Solutions and Workarounds*
Users can download the latest version of Google SketchUp from
http://sketchup.google.com
7. *Credits*
This vulnerability was discovered and researched by Francisco Falcon
from Core Security Technologies during Bugweek 2009 [1].
The publication of this advisory was coordinated by Jorge Lucangeli Obes
from Core Security Advisories team.
8. *Technical Description / Proof of Concept Code*
8.1. *Introduction*
Google SketchUp [2] is a 3D modeling program designed for architects,
civil engineers, filmmakers, game developers, and related professions.
Google SketchUp bundles an old version of 'lib3ds', a library used to
process 3DS files. This library is being compiled in a way that leads to
improper validation of data when importing 3DS files; this condition can
be exploited by remote attackers to trigger a memory corruption
vulnerability by enticing an unsuspecting user to open a specially
crafted 3DS file, possibly leading to arbitrary code execution.
When processing certain structures from a 3DS file, Google SketchUp
trusts bytes from the 3DS file without performing validations and uses
them as:
1. an operand in pointer arithmetics to calculate an index for an
array where user-controlled data will be written.
2. a loop counter in a copy operation.
These bytes are used by the application without proper validation of
their values, leading to the issues described below.
8.2. *Memory Corruption*
While importing 3DS files, Google SketchUp reads a sequence of 2-byte
words from the .3DS file, starting at offset 0x6F49F. These words are
used as operands in pointer arithmetics to calculate an index for an
array where data will be copied to. However, the application does not
check if the calculated index is inside the bounds of the destination
array. By crafting a 3DS file with large values for the words located at
the mentioned offset, the lack of bounds-checking can be exploited to
write data outside the limits of the array, leading to a memory
corruption vulnerability.
The following disassembled code of the Google SketchUp 3DS Importer
module illustrates the vulnerability. As we can see, the data that is
copied into the array is fetched from the 3DS file starting at offset
0x6F491. That means that if the memory corruption vulnerability is
triggered, the data that will overwrite memory contents is fully
controlled by the attacker.
/-----
[Module:3DSImporter.dll]
0603AD86 |. 8B97 A8000000 ||MOV EDX,DWORD PTR DS:[EDI+A8] ;
EDX = pointer to destination array
0603AD8C |. 0FB7C0 ||MOVZX EAX,AX ;
AX = words starting at offset 0x6F49F (user-controlled)
0603AD8F |. 83C4 04 ||ADD ESP,4
0603AD92 |. 6BC0 5C ||IMUL EAX,EAX,5C
0603AD95 |. 8D4C24 18 ||LEA ECX,DWORD PTR SS:[ESP+18]
0603AD99 |. 8D5410 04 ||LEA EDX,DWORD PTR DS:[EAX+EDX+4] ;
calculates the index of the array where it will write, using
user-controlled data
0603AD9D |. 8D49 00 ||LEA ECX,DWORD PTR DS:[ECX]
0603ADA0 |> 8A01 ||/MOV AL,BYTE PTR DS:[ECX] ;
reads bytes starting at offset 0x6F491 (user-controlled)
0603ADA2 |. 8802 |||MOV BYTE PTR DS:[EDX],AL ;
copies the byte in AL to the array; *MEMORY CORRUPTION OCCURS HERE*
0603ADA4 |. 83C1 01 |||ADD ECX,1
0603ADA7 |. 83C2 01 |||ADD EDX,1
0603ADAA |. 84C0 |||TEST AL,AL
0603ADAC |.^ 75 F2 ||\JNZ SHORT 3DSImpor.0603ADA0 ;
keep copying till AL = 0
- -----/
8.2.1. *Vulnerable function*
We believe the vulnerable function to be face_array_read(), starting at
line 238 in file src/lib3ds_mesh.c of lib3ds. The vulnerable code is
executed in case CHK_MSH_MAT_GROUP of the following switch statement:
/-----
[Function:src/lib3ds_mesh.c:face_array_read()]
switch (chunk) {
case CHK_MSH_MAT_GROUP: {
char name[64];
unsigned n;
unsigned i;
int index;
int material;
lib3ds_io_read_string(io, name, 64);
material = lib3ds_file_material_by_name(file, name);
n = lib3ds_io_read_word(io);
for (i = 0; i < n; ++i) {
index = lib3ds_io_read_word(io);
if (index < mesh->nfaces) { /* <- bounds check */
mesh->faces[index].material = material;
} else {
// TODO warning
}
}
break;
}
- -----/
In the latest version of lib3ds, version 2.0, there is a bounds check
present in the code. However, in the vulnerable Google SketchUp binary
there is no bounds check. Moreover, the assignment following the bounds
check does not match the assembler in the binary, but the previous
version of lib3ds does match. In version 1.3.0 of lib3ds 'strcpy' is
used to copy a string into the array, which is what the binary shows.
The file is lib3ds/mesh.c, line 77:
/-----
[Function:lib3ds/mesh.c:77]
case LIB3DS_MSH_MAT_GROUP:
{
char name[64];
unsigned faces;
unsigned i;
unsigned index;
if (!lib3ds_io_read_string(io, name, 64)) {
return(LIB3DS_FALSE);
}
faces=lib3ds_io_read_word(io);
for (i=0; i