Headline
CVE-2019-5059: TALOS-2019-0843 || Cisco Talos Intelligence Group
An exploitable code execution vulnerability exists in the XPM image rendering functionality of SDL2_image 2.0.4. A specially crafted XPM image can cause an integer overflow, allocating too small of a buffer. This buffer can then be written out of bounds resulting in a heap overflow, ultimately ending in code execution. An attacker can display a specially crafted image to trigger this vulnerability.
Summary
An exploitable code execution vulnerability exists in the XPM image rendering functionality of SDL2_image 2.0.4. A specially crafted XPM image can cause an integer overflow, allocating too small of a buffer. This buffer can then be written out of bounds resulting in a heap overflow, ultimately ending in code execution. An attacker can display a specially crafted image to trigger this vulnerability.
Tested Versions
SDL_image 2.0.4
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-190: Integer Overflow or Wraparound
Details
LibSDL is a multi-platform library for easy access to low-level hardware and graphics, providing support for a large number of video games, software and emulators. The LibSDL2_Image library is an optional component that deals specifically with parsing and displaying a variety of image file formats, creating a single, uniform API for image processing, regardless of the type.
XPM is a line-based image format. The first line contains the width, height, number of colors, and number of characters per pixel to be parsed [0].
SDL_image/IMG_xpm.c:1015
/*
* The header string of an XPMv3 image has the format
*
* <width> <height> <ncolors> <cpp> [ <hotspot_x> <hotspot_y> ]
*
* where the hotspot coords are intended for mouse cursors.
* Right now we don't use the hotspots but it should be handled
* one day.
*/
if (SDL_sscanf(line, "%d %d %d %d", &w, &h, &ncolors, &cpp) != 4 // [0]
|| w <= 0 || h <= 0 || ncolors <= 0 || cpp <= 0) {
error = "Invalid format description";
goto done;
}
In preparation for parsing, a buffer is allocated for the pixels by multiplying the read number of colors by the characters per pixel [1].
SDL_image/IMG_xpm.c:1033
keystrings = (char *)SDL_malloc(ncolors * cpp); // [1]
if (!keystrings) {
error = "Out of memory";
goto done;
}
nextkey = keystrings;
The next line in the XPM format describes each color. The most basic format for each of these lines is to have a character with a given color. For instance, the following line shows that any following ‘z’ will be colored black and will produce a line of colors that has three black pixels, three white pixels and then three more black pixels.
z c #000000
. c #ffffff
zzz...zzz
When parsing these lines [2], the color code is copied into the keystrings/nextkey buffer allocated previously [3]. The amount of data copied is controlled via the cpp value read from the previous line assuming the first characters of the color code is valid [4].
SDL_image/IMG_xpm.c:1070
line = get_next_line(xpmlines, src, 0); // [2]
if (!line)
goto done;
p = line + cpp + 1;
/* parse a colour definition */
for (;;) {
char nametype;
char *colname;
Uint32 rgb, pixel;
SKIPSPACE(p);
if (!*p) {
printf("Color parse error\n");
error = "colour parse error";
goto done;
}
nametype = *p;
SKIPNONSPACE(p);
SKIPSPACE(p);
colname = p;
SKIPNONSPACE(p);
if (nametype == 's') {
printf("skipping nametype == s\n");
continue; /* skip symbolic colour names */
}
if (!color_to_rgb(colname, (int)(p - colname), &rgb)) { // [4]
printf("color to rgb failed\n");
continue;
}
SDL_memcpy(nextkey, line, cpp); // [3]
By providing a sufficiently large ncolors and cpp value, the buffer allocation size can overflow into a size too small to hold the color code string. This causes the memcpy to cause a heap overflow, potentially resulting in code execution.
Crash Information
=================================================================
==21578==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000eff4 at pc 0x7fa51007e904 bp 0x7ffe5004dbd0 sp 0x7ffe5004d378
WRITE of size 4100 at 0x60200000eff4 thread T0
#0 0x7fa51007e903 in __asan_memcpy (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x8c903)
#1 0x4ba104 in load_xpm ../IMG_xpm.c:1123
#2 0x40653c in IMG_LoadTyped_RW ../IMG.c:195
#3 0x404d70 in main ../showimage.c:56
#4 0x7fa50f51e82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#5 0x405388 in _start (/root/vmshare/targets/sdl/SDL_image/build-afl-asan/showimage+0x405388)
0x60200000eff4 is located 0 bytes to the right of 4-byte region [0x60200000eff0,0x60200000eff4)
allocated by thread T0 here:
#0 0x7fa51008a602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
#1 0x4f6ebc in SDL_malloc_REAL /root/vmshare/targets/sdl/SDL/src/stdlib/SDL_malloc.c:5390
SUMMARY: AddressSanitizer: heap-buffer-overflow ??:0 __asan_memcpy
Shadow bytes around the buggy address:
0x0c047fff9da0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9db0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9dc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9dd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9de0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c047fff9df0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa[04]fa
0x0c047fff9e00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9e10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9e20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9e30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9e40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Heap right redzone: fb
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
==21578==ABORTING
Timeline
2019-05-30 - Vendor Disclosure
2019-07-03 - Vendor Patched
2019-07-29 - Public Release
Discovered by Cory Duplantis of Cisco Talos.