The FLIC parser is vulnerable to code execution in the "open file" dialog. Gimp creates a preview for the user when a file is selected, but before actually opening it. The worst part is that a properly crafted file won't crash the parser, making this attack hard to detect on vulnerable systems.
By this time, I am not aware of a successful attack on 64 bit systems and/or with activated ASLR. Circumventing ASLR would need a more sophisticated approach, including address leaks which seem to be quite unfeasable with the memory layout of the FLIC parser. If you are aware of such an attack vector, please contact me.
To mount a successful attack, I decided to use the latest FreeBSD release (11.1) on a 32 bit environment. As FreeBSD is one of the last systems which do not support ASLR by default, it allowed us to prepare an attack with fixed addresses. The 32 bit environment also allowed us to easily overflow the address range, overwriting arbitrary memory locations.
The FLIC file format is used for animations, consisting of multiple frames per file. A frame can be compressed by using an RLE algorithm, which was a popular way of compressing files many many years ago.
The Gimp parser fails to properly check boundaries, which allows out of boundary writes. Even worse, the memory write operations do not have to be continuous. Instead, parts of the image can be skipped before writing the next chunk of bytes into RAM.
You can see the function fli_read_lc here:
unsigned short yc, firstline, numline; unsigned char *pos; memcpy(framebuf, old_framebuf, fli_header->width * fli_header->height); firstline = fli_read_short(f); numline = fli_read_short(f); for (yc=0; yc < numline; yc++) { unsigned short xc, pc, pcnt; pc=fli_read_char(f); xc=0; pos=framebuf+(fli_header->width * (firstline+yc)); for (pcnt=pc; pcnt>0; pcnt--) { unsigned short ps,skip; skip=fli_read_char(f); ps=fli_read_char(f); xc+=skip; if (ps & 0x80) { unsigned char val; ps=-(signed char)ps; val=fli_read_char(f); memset(&(pos[xc]), val, ps); xc+=ps; } else { fread(&(pos[xc]), ps, 1, f); xc+=ps; } } }
The width of an image in Gimp cannot be larger than 2^18. But as you can see, firstline and numline, which are values between 0 and 2^16, are not validated to be within the height limit. For each line in question, there can be 2^8 instructions, each instruction skipping up to 2^8 bytes in the target memory and writing not more than 2^8 bytes.
Which means that our attack can cover around 2^50 bytes, which is a lot, but not enough to overflow the address range of 64 bit sytems. Therefore I chose to attack 32 bit systems, having the whole address range under control.
I decided to go for a PLT/GOT attack, i.e. overwriting the actual location of a chosen library function to trigger a system(3) call with a string contained within the image, leading to full flexibility of programs to execute.
The function fli_read_lc can be triggered multiple times in a row during the preview generation. Our idea was to modify the GOT entry of memset, overwriting it with system. Fortunately the rest of the code does not access memset directly during iterations of fli_read_lc, giving us a good foundation for our attack. Also, memset uses attacker-controlled memory for the first argument, which therefore could hold our string of a program to execute, e.g. "/usr/local/bin/xcalc".
The attack is performed in three steps:
To cover our traces, step 4 could actually overwrite the framebuf content with an actually usable picture.