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.