Headline
CVE-2017-14442: TALOS-2017-0491 || Cisco Talos Intelligence Group
An exploitable code execution vulnerability exists in the BMP image rendering functionality of SDL2_image-2.0.2. A specially crafted BMP image can cause a stack overflow resulting in code execution. An attacker can display a specially crafted image to trigger this vulnerability.
Summary
An exploitable code execution vulnerability exists in the BMP image rendering functionality of SDL2_image-2.0.2. A specially crafted BMP image can cause a stack overflow resulting in code execution. An attacker can display a specially crafted image to trigger this vulnerability.
Tested Versions
Simple DirectMedia Layer SDL2_image 2.0.2
Product URLs
https://www.libsdl.org/projects/SDL_image/
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-121: Stack-based Buffer Overflow
Details
LibSDL is a multi-platform library for easy access to low level hardware and graphics, providing support for a large amount of games, software, and emulators. The last known count of software using LibSDL (from 2012) listed the number at upwards of 120. The LibSDL2_Image library is an optional component that deals specifically with parsing and displaying a variety of image file formats, creating a single and uniform API for image processing, regardless of the type.
When reading in a BMP file, libSDL2_Image must populate a color palette object for the image. As per the BMP file format, one of the headers read in is the “biClrUsed” field, which designates how many colors are within the color palette. This is read by the following code:
//IMG_bmp.c:683
biSize = SDL_ReadLE32(src); //offset 0x16 in file
if (biSize == 40) {
biWidth = SDL_ReadLE32(src); //0x1A
biHeight = SDL_ReadLE32(src); //0x1E
biPlanes = SDL_ReadLE16(src); //0x23
biBitCount = SDL_ReadLE16(src); //0x25
biCompression = SDL_ReadLE32(src);
biSizeImage = SDL_ReadLE32(src);
biXPelsPerMeter = SDL_ReadLE32(src); //0x2e
biYPelsPerMeter = SDL_ReadLE32(src); //0x32
biClrUsed = SDL_ReadLE32(src); //0x36 [1]
biClrImportant = SDL_ReadLE32(src); //0x3a
At [1], the biClrUsed field is read in as an unsigned 32-bit integer. After this, the variable is never mentioned again, at least until it is used to populate the palette array as such:
if (biBitCount <= 8) {
if (biClrUsed == 0) {
biClrUsed = 1 << biBitCount;
}
for (i = 0; i < (int) biClrUsed; ++i) { //[1]
SDL_RWread(src, &palette[i], 4, 1);
}
Since there isn’t any validation on the “biClrUsed” field at all, the result is a stack based buffer overflow completely under control of an attacker.
Crash Information
$rax : 0x019c3c51016b173b -> 0x019c3c51016b173b
$rbx : 0x0000000000000000 -> 0x0000000000000000
$rcx : 0x0000000001e28b77 -> 0x0000000001e28b77
$rdx : 0x0000000000000004 -> 0x0000000000000004
$rsp : 0x00007ffe43fe6810 -> 0x0000000100000000 -> 0x0000000100000000
$rbp : 0x00007ffe43fe6cd0 -> 0x00007ffe43fe6cf0 -> 0x00007ffe43fe6d30 -> 0x00007ffe43fe6d60 -> 0x00007ffe43fe6da0 ->
0x0000000000000000
-> 0x0000000000000000
$rsi : 0x0000000002735c60 -> 0x0000000000000000 -> 0x0000000000000000
$rdi : 0x00007ffe43fe6cc0 -> 0x0000000000000000 -> 0x0000000000000000
rip : 0x00007f8c982f6105 -> <LoadICOCUR_RW+1197> mov rdx, QWORD PTR [rax+0x20]
$r8 : 0x0000000002735c60 -> 0x0000000000000000 -> 0x0000000000000000
$r9 : 0x00007ffe43fe6cbc -> 0x0000000001e28b78 -> 0x0000000001e28b78
$r10 : 0x0000000000ff0000 -> 0x0000000000ff0000
$r11 : 0x00007f8c9856d5ce -> 0x20ec8348e5894855 -> 0x20ec8348e5894855
$r12 : 0x0000000000400a10 -> <_start+0> xor ebp, ebp
$r13 : 0x00007ffe43fe6e80 -> 0x0000000000000002 -> 0x0000000000000002
$r14 : 0x0000000000000000 -> 0x0000000000000000
$r15 : 0x0000000000000000 -> 0x0000000000000000
$eflags: [CARRY parity adjust zero SIGN trap INTERRUPT direction overflow RESUME virtualx86 identification]
---------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------[ stack ]----
0x00007ffe43fe6810|+0x00: 0x0000000100000000 -> 0x0000000100000000 <-$rsp
0x00007ffe43fe6818|+0x08: 0x0000000002756200 -> 0x00007f8c98578e48 -> 0x20ec8348e5894855 -> 0x20ec8348e5894855
0x00007ffe43fe6820|+0x10: 0x000000000000000b -> 0x000000000000000b
0x00007ffe43fe6828|+0x18: 0x0000068800000000 -> 0x0000068800000000
0x00007ffe43fe6830|+0x20: 0x01c2989401703453 -> 0x01c2989401703453
0x00007ffe43fe6838|+0x28: 0x01dd8e7701df886f -> 0x01dd8e7701df886f
0x00007ffe43fe6840|+0x30: 0x0163234e015e1b45 -> 0x0163234e015e1b45
0x00007ffe43fe6848|+0x38: 0x01753b6b01692b59 -> 0x01753b6b01692b59
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------[ code:i386:x86-64 ]----
0x7f8c982f60f4 <LoadICOCUR_RW+1180> rol BYTE PTR [rbx-0x74fe13bb], 1
0x7f8c982f60fa <LoadICOCUR_RW+1186> rex.RB (bad)
0x7f8c982f60fc <LoadICOCUR_RW+1188> cmp eax, DWORD PTR [rbp-0x14]
0x7f8c982f60ff <LoadICOCUR_RW+1191> jg 0x7f8c982f60c2 <LoadICOCUR_RW+1130>
0x7f8c982f6101 <LoadICOCUR_RW+1193> mov rax, QWORD PTR [rbp-0x20]
->0x7f8c982f6105 <LoadICOCUR_RW+1197> mov rdx, QWORD PTR [rax+0x20]
0x7f8c982f6109 <LoadICOCUR_RW+1201> mov rax, QWORD PTR [rbp-0x20]
0x7f8c982f610d <LoadICOCUR_RW+1205> mov ecx, DWORD PTR [rax+0x14]
0x7f8c982f6110 <LoadICOCUR_RW+1208> mov rax, QWORD PTR [rbp-0x20]
0x7f8c982f6114 <LoadICOCUR_RW+1212> mov eax, DWORD PTR [rax+0x18]
0x7f8c982f6117 <LoadICOCUR_RW+1215> imul eax, ecx
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------[ source:IMG_bmp.c+761 ]----
757 }
758 }
759
760 /* Read the surface pixels. Note that the bmp image is upside down */
// surface=0x00007ffe43fe6cb0 -> [...] -> 0x019c3c51016b173b, bits=0x00007ffe43fe6ca8 -> [...] -> 0x01e4b28b01f7d1a1
-> 761 bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch); //points immediately after 0x1000 buffer
762 switch (ExpandBMP) {
763 case 1:
764 bmpPitch = (biWidth + 7) >> 3;
765 pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------[ threads ]----
[#0] Id 1, Name: "", stopped, reason: SIGSEGV
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------[ trace ]----
[#0] 0x7f8c982f6105->Name: LoadICOCUR_RW(src=0x2756200, type=0x1, freesrc=0x0)
[#1] 0x7f8c982f6651->Name: IMG_LoadICO_RW(src=0x2756200)
[#2] 0x7f8c982f47ef->Name: IMG_LoadTyped_RW(src=0x2756200, freesrc=0x1, type=0x7ffe43fe8459 "/<(^_^)>")
[#3] 0x7f8c982f45e0->Name: IMG_Load(file=0x7ffe43fe8458 "./<(^_^)>")
[#4] 0x400b85->Name: main(argc=0x2, argv=0x7ffe43fe6e88)
-----------------------------------------------------------------------------------------------------------------------------------------
Timeline
2017-11-28 - Vendor Disclosure
2018-03-01 - Public Release
Discovered by Yves Younan and Lilith of Cisco Talos.