Talos Vulnerability Report

TALOS-2019-0821

Simple DirectMedia Layer SDL2_image IMG_LoadPCX_RW signed comparison code execution vulnerability

July 2, 2019
CVE Number

CVE-2019-5052

Summary

An exploitable integer overflow vulnerability exists when loading a PCX file in SDL2_image 2.0.4. A specially crafted file can cause an integer overflow, resulting in too little memory being allocated, which can lead to a buffer overflow and potential code execution. An attacker can provide a specially crafted image file to trigger this vulnerability.

Tested Versions

Simple DirectMedia Layer SDL2_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

This vulnerability is present in the SDL2_image library, which is used for loading images in different formats.

There is a vulnerability in the function responsible for loading PCX files. A specially crafted PCX file can lead to a heap buffer overflow and remote code execution.

Let's investigate this vulnerability. After we attempt to load a malformed PCX file, the following state appears:

    ./showimage PoC.pcx
==12669==ERROR: AddressSanitizer: attempting free on address which was not malloc()-ed: 0xa896ce70 in thread T0
    #0 0xb7245a84 in free (/usr/lib/i386-linux-gnu/libasan.so.2+0x96a84)
    #1 0x81259b3 in SDL_free_REAL /home/icewall/bugs/SDL_src/SDL2-2.0.9/src/stdlib/SDL_malloc.c:5372
    #2 0x8131dd2 in SDL_FreePalette_REAL /home/icewall/bugs/SDL_src/SDL2-2.0.9/src/video/SDL_pixels.c:735
    #3 0x8131b32 in SDL_SetPixelFormatPalette_REAL /home/icewall/bugs/SDL_src/SDL2-2.0.9/src/video/SDL_pixels.c:685
    #4 0x813d1f5 in SDL_SetSurfacePalette_REAL /home/icewall/bugs/SDL_src/SDL2-2.0.9/src/video/SDL_surface.c:221
    #5 0x81448d1 in SDL_FreeSurface_REAL /home/icewall/bugs/SDL_src/SDL2-2.0.9/src/video/SDL_surface.c:1233
    #6 0x80abfd7 in SDL_FreeSurface /home/icewall/bugs/SDL_src/SDL2-2.0.9/src/dynapi/SDL_dynapi_procs.h:475
    #7 0x805875f in IMG_LoadPCX_RW ../IMG_pcx.c:252
    #8 0x804c301 in IMG_LoadTyped_RW ../IMG.c:195
    #9 0x804beff in IMG_Load ../IMG.c:136
    #10 0x804c3e7 in IMG_LoadTexture ../IMG.c:212
    #11 0x804b65c in main ../showimage.c:101
    #12 0xb6f99636 in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x18636)
    #13 0x804ad50  (/home/icewall/bugs/SDL_src/SDL2_image-2.0.4/build/showimage+0x804ad50)

0xa896ce70 is located 0 bytes inside of 536870912-byte region [0xa896ce70,0xc896ce70)

It looks like the heap has been corrupted, and application is crashing while free-ing. Let's take a closer look at the code where the vulnerability takes place:

IMG_pcx.c
146    bpl = pcxh.NPlanes * pcxh.BytesPerLine;
147    if (bpl > surface->pitch) {
148      error = "bytes per line is too large (corrupt?)";
149    }    
150    buf = (Uint8 *)SDL_calloc(SDL_max(bpl, surface->pitch), 1);
151    row = (Uint8 *)surface->pixels;  
152    for ( y=0; y<surface->h; ++y ) {
153        /* decode a scan line to a temporary buffer first */
154        int i, count = 0;
155        Uint8 ch;
156        Uint8 *dst = (src_bits == 8) ? row : buf;
157        if ( pcxh.Encoding == 0 ) {
158            if(!SDL_RWread(src, dst, bpl, 1)) {
159                error = "file truncated";
160                goto done;
161            }
162        } else {
163            for(i = 0; i < bpl; i++) {
164                if(!count) {
165                    if(!SDL_RWread(src, &ch, 1, 1)) {
166                        error = "file truncated";
167                        goto done;
168                    }
169                    if( (ch & 0xc0) == 0xc0) {
170                        count = ch & 0x3f;
171                        if(!SDL_RWread(src, &ch, 1, 1)) {
172                            error = "file truncated";
173                            goto done;
174                        }
175                    } else
176                        count = 1;
177                }
178                dst[i] = ch;
179                count--;
180            }
181        }

        (...)

214        row += surface->pitch;
215    }

The overflow appears at line 158. It happens because, as you can see at line 147, the calculated bpl value is checked against the surface->pitch field. Because both variables are of type signed integer, there is a scenario where e.g bpl = -1 and surface->pitch = 1, which causes that condition to be satisfied.

Later, assuming that src_bits is equal to 8, the dst pointer will have the value of row, where row == surface->pixels.

Now, calling SDL_RWread function at line 158 with a size argument equal bpl, bpl's signed value will be treated as a size_t (unsigned), which consequently leads to reading UINT_MAX bytes from the file.

A dump of a malformed PCX file:

struct PCX file         0h  80h Fg: Bg: 
ubyte Manufacturer      10  0h  1h  Fg: Bg: 
enum PCX_TYPE version   v3up (5)    1h  1h  Fg: Bg: 
ubyte encoding          0   2h  1h  Fg: Bg: 
ubyte BitsPerPixel      1   3h  1h  Fg: Bg: 
ushort Xmin             0   4h  2h  Fg: Bg: 
ushort Ymin             0   6h  2h  Fg: Bg: 
ushort Xmax             0   8h  2h  Fg: Bg: 
ushort Ymax             257 Ah  2h  Fg: Bg: 
ushort HDpi             511 Ch  2h  Fg: Bg: 
ushort VDpi             257 Eh  2h  Fg: Bg: 
ubyte Colormap[48]      10h 30h Fg: Bg: 
ubyte Reserved          255 40h 1h  Fg: Bg: 
ubyte NPlanes           1   41h 1h  Fg: Bg: 
ushort BytesPerLine     32769   42h 2h  Fg: Bg: 
ushort PaletteInfo      18  44h 2h  Fg: Bg: 
ushort HscreenSize      0   46h 2h  Fg: Bg: 
ushort VscreenSize      0   48h 2h  Fg: Bg: 
ubyte Filler[54]        4Ah 36h Fg: Bg: 

An attacker fulling controlling the size of overwrite (via size of the file) and its content, can turn this heap-based buffer overflow in a remote code execution.

Crash Information

    ./showimage PoC.pcx
==12669==ERROR: AddressSanitizer: attempting free on address which was not malloc()-ed: 0xa896ce70 in thread T0
    #0 0xb7245a84 in free (/usr/lib/i386-linux-gnu/libasan.so.2+0x96a84)
    #1 0x81259b3 in SDL_free_REAL /home/icewall/bugs/SDL_src/SDL2-2.0.9/src/stdlib/SDL_malloc.c:5372
    #2 0x8131dd2 in SDL_FreePalette_REAL /home/icewall/bugs/SDL_src/SDL2-2.0.9/src/video/SDL_pixels.c:735
    #3 0x8131b32 in SDL_SetPixelFormatPalette_REAL /home/icewall/bugs/SDL_src/SDL2-2.0.9/src/video/SDL_pixels.c:685
    #4 0x813d1f5 in SDL_SetSurfacePalette_REAL /home/icewall/bugs/SDL_src/SDL2-2.0.9/src/video/SDL_surface.c:221
    #5 0x81448d1 in SDL_FreeSurface_REAL /home/icewall/bugs/SDL_src/SDL2-2.0.9/src/video/SDL_surface.c:1233
    #6 0x80abfd7 in SDL_FreeSurface /home/icewall/bugs/SDL_src/SDL2-2.0.9/src/dynapi/SDL_dynapi_procs.h:475
    #7 0x805875f in IMG_LoadPCX_RW ../IMG_pcx.c:252
    #8 0x804c301 in IMG_LoadTyped_RW ../IMG.c:195
    #9 0x804beff in IMG_Load ../IMG.c:136
    #10 0x804c3e7 in IMG_LoadTexture ../IMG.c:212
    #11 0x804b65c in main ../showimage.c:101
    #12 0xb6f99636 in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x18636)
    #13 0x804ad50  (/home/icewall/bugs/SDL_src/SDL2_image-2.0.4/build/showimage+0x804ad50)

0xa896ce70 is located 0 bytes inside of 536870912-byte region [0xa896ce70,0xc896ce70)
ASAN:SIGSEGV
==12669==AddressSanitizer: while reporting a bug found another one. Ignoring.

Timeline

2019-05-08 - Vendor Disclosure
2019-07-01 - Vendor Patched; Public Release

Credit

Discovered by Marcin 'Icewall' Noga of Cisco Talos.