Talos Vulnerability Report

TALOS-2017-0489

Simple DirectMedia Layer SDL2_image ILBM CMAP Parsing Code Execution Vulnerability

March 1, 2018
CVE Number

CVE-2017-14440

Summary

An exploitable code execution vulnerability exists in the ILBM image rendering functionality of SDL2_image-2.0.2. A specially crafted ILBM 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, including “Angry Birds”, “Unreal Tournament”, “VisualBoyAdvance” and “VLC”. The last known count of software using LibSDL (from 2012) listed the number at upwards of 120. The LibSDL2_Image library subcomponent 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 an ILBM file to be displayed, as per the ILBM file format (https://en.wikipedia.org/wiki/ILBM#BMHD:_Bitmap_Header)[ https://en.wikipedia.org/wiki/ILBM#BMHD:_Bitmap_Header], a “CMAP” attribute must be parsed. LibSDL2_Image does this by doing a memcmp of four bytes with the current offset into the file read, and then reading the following bytes into the colormap variable with the following code:

if ( !SDL_memcmp( id, “CMAP”, 4 ) ) /* palette ( Color Map ) */
    	{
        	if ( !SDL_RWread( src, &colormap, size, 1 ) )

The SDL_Rwread function is essentially a call to fread as such:

nread = fread(ptr, size, maxnum, context->hidden.stdio.fp);

Unfortunately, the size variable, which determines how much is read from the image file into the colormap stack variable, has no checks on it whatsoever.

     if ( !SDL_RWread( src, &size, 4, 1 ) ) [1]
    {   
        error="error reading IFF chunk size";
        goto done;
    }
    bytesloaded = 0;
    size = SDL_SwapBE32( size ); [2]

It’s taken as 4 bytes straight from the file [1] and then reversed in byte order [2]. As mentioned before, once a “CMAP” tag is found inside of the file, it will attempt to copy ‘size’ bytes into the &colormap, which is defined as such:

SDL_Surface *IMG_LoadLBM_RW( SDL_RWops *src )
{
  	  Sint64 start;
   	 SDL_Surface *Image;
	Uint8       id[4], pbm, colormap[MAXCOLORS*3], *MiniBuf, *ptr, count, color, msk;

With the MAXCOLOR macro defined as 255. Thus, for any ‘size’ variable greater than 0x300, there will be an unrestricted stack based overflow with attacker controlled data.

Crash Information

--------------------------------------------------------------------------[ registers ]----
$rax   : 0x0000000000000000
$rbx   : 0x0000000000000000
$rcx   : 0x00007ffff7b95fa2 -> 0x7720726f72724500
$rdx   : 0xd4ff668104110000
$rsp   : 0x00007fffffffdc78 -> 0x00007ffff7afcd0e -> <SDL_free_REAL+37> mov esi, 0xffffffff
$rbp   : 0x00007fffffffdc90 -> 0x00007fffffffdcb0 -> 0x00007fffffffe0a0 -> 0x40fd3a95294020c4
$rsi   : 0x00007ffff7b95f85 -> "Error reading from datastream"
$rdi   : 0xd4ff668104110000
$rip   : 0x00007ffff74f6614 -> <free+20> mov rax, QWORD PTR [rdi-0x8]
$r8    : 0x0000000000000004
$r9    : 0x00007ffff7fd6700 -> 0x00007ffff7fd6700 -> [loop detected]
$r10   : 0x00007fffffffda80 -> 0x0000000000000000
$r11   : 0x00007ffff7aa0ad0 -> <SDL_free+0> push rbp
$r12   : 0x0000000000400a10 -> <_start+0> xor ebp, ebp
$r13   : 0x00007fffffffe230 -> 0xfbaffffeffa7a323
$r14   : 0x0000000000000000
$r15   : 0x0000000000000000
$eflags: [carry PARITY adjust zero SIGN trap INTERRUPT direction overflow RESUME virtualx86 identification]
------------------------------------------------------------------------------[ stack ]----
0x00007fffffffdc78|+0x00: 0x00007ffff7afcd0e -> <SDL_free_REAL+37> mov esi, 0xffffffff  <-$rsp
0x00007fffffffdc80|+0x08: 0x0000000000000001
0x00007fffffffdc88|+0x10: 0xd4ff668104110000
0x00007fffffffdc90|+0x18: 0x00007fffffffdcb0 -> 0x00007fffffffe0a0 -> 0x40fd3a95294020c4        <-$rbp
0x00007fffffffdc98|+0x20: 0x00007ffff7aa0aec -> <SDL_free+28> leave 
0x00007fffffffdca0|+0x28: 0x00007ffff7fd6700 -> 0x00007ffff7fd6700 -> [loop detected]
0x00007fffffffdca8|+0x30: 0xd4ff668104110000
0x00007fffffffdcb0|+0x38: 0x00007fffffffe0a0 -> 0x40fd3a95294020c4
-------------------------------------------------------------------[ code:i386:x86-64 ]----
0x7ffff74f6607 <free+7>         mov    rax, QWORD PTR [rax]
0x7ffff74f660a <free+10>        test   rax, rax
0x7ffff74f660d <free+13>        jne    0x7ffff74f6682 <__GI___libc_free+130>
0x7ffff74f660f <free+15>        test   rdi, rdi
0x7ffff74f6612 <free+18>        je     0x7ffff74f6680 <__GI___libc_free+128>
->0x7ffff74f6614 <free+20>        mov    rax, QWORD PTR [rdi-0x8]
0x7ffff74f6618 <free+24>        lea    rsi, [rdi-0x10]
0x7ffff74f661c <free+28>        test   al, 0x2
0x7ffff74f661e <free+30>        jne    0x7ffff74f6640 <__GI___libc_free+64>
0x7ffff74f6620 <free+32>        test   al, 0x4
0x7ffff74f6622 <free+34>        lea    rdi, [rip+0x328ff7]        # 0x7ffff781f620 <main_arena>
----------------------------------------------------------------------------[ threads ]----
[#0] Id 5, Name: "img_read_plain", stopped, reason: SIGSEGV
[#1] Id 4, Name: "img_read_plain", stopped, reason: SIGSEGV
#2] Id 3, Name: "img_read_plain", stopped, reason: SIGSEGV
[#3] Id 2, Name: "img_read_plain", stopped, reason: SIGSEGV
[#4] Id 1, Name: "img_read_plain", stopped, reason: SIGSEGV
------------------------------------------------------------------------------[ trace ]----
#0 __GI___libc_free (mem=0xd4ff668104110000) at malloc.c:2929
#1 0x00007ffff7afcd0e in SDL_free_REAL (ptr=0xd4ff668104110000) at /root/work_work/triages/libsdl/SDL2-2.0.7/src/stdlib/    
SDL_malloc.c:5372
#2 0x00007ffff7aa0aec in SDL_free (a=0xd4ff668104110000) at /root/work_work/triages/libsdl/SDL2-2.0.7/src/dynapi/   
SDL_dynapi_procs.h:408
#3 0x00007ffff782da5f in IMG_LoadLBM_RW (src=0xc4da70) at IMG_lbm.c:466
#4 0xffffaefdff7efe68 in ?? ()
#5 0x252180a6bdd00dad in ?? ()
#6 0x3a75d972bccbffdc in ?? ()
#7 0x0000d2ffae89353f in ?? ()
#8 0xff6f8104110038fe in ?? ()
#9 0xae561f5bbdf6a6ba in ?? ()
#10 0x95d1f6f57ebfd5bf in ?? ()
#11 0xfdefeffefdfdfb8b in ?? ()

Timeline

2017-11-28 - Vendor Disclosure
2018-03-01 - Public Release

Credit

Discovered by Lilith <(^~^)> of Cisco Talos.