Talos Vulnerability Report

TALOS-2019-0843

SDL_image XPM image color code code execution vulnerability

July 29, 2019
CVE Number

CVE-2019-5059

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

Credit

Discovered by Cory Duplantis of Cisco Talos.