CVE-2019-5052
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.
Simple DirectMedia Layer SDL2_image 2.0.4
https://www.libsdl.org/projects/SDL_image/
8.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CWE-190: Integer Overflow or Wraparound
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.
./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.
2019-05-08 - Vendor Disclosure
2019-07-01 - Vendor Patched; Public Release
Discovered by Marcin 'Icewall' Noga of Cisco Talos.