[1days] [0days] [PoCs] More gstreamer FLIC / vmnc issues

A part of any intellectually honest full disclosure experiment is to disclose the less interesting findings alongside the more serious issues and exploits.

Accordingly, if you were looking for spectacular 0day exploits, this is not the post you are looking for. If you’re generally interested in software failure conditions, though, here’s a bunch.
While looking at the gstreamer FLIC and vmnc decoders, I noticed various other issues aside from the ones that have already been blogged about. Some of these issues are also serious while others are trivial. For most of the issues, I have to commend the gstreamer team on a great job looking for variants of my initial post and in particular giving the FLIC decoder a thorough examination. For many security “fixes”, a project or vendor will just patch up the immediate issues reported, but gstreamer looked at the surrounding area of the initial fault and patched up many additional issues independently. Therefore, most of the vulnerabilities disclosed in this post are now 1days and not 0days.

1: FLIC decoder: uninitialized output canvas
Looks like it’s still an 0day. Images are from Fedora 25 with all updates applied.

This one goes first because it is nice and visual. The bug is that the output canvas is allocated but not initialized. Therefore if you have a file that does not start off by clearing the screen, or rendering an entire first frame, you will have uninitialized heap memory in the output canvas. It looks kind of pretty. Is that pointers I see? :-)


2: FLIC decoder: out-of-bounds writes in FLX_SS2 command
A near identical vulnerability to the one I exploited in the FLX_LC command. Caught and fixed by gstreamer upstream; patched in latest Ubuntu and Fedora updates.

3: FLIC decoder: integer underflow and subsequent wildness in FLX_BRUN command
Caught and fixed by gstreamer upstream; patched in latest Ubuntu and Fedora updates.

The faulty code:

 gulong count, lines, row;
   row = flxdec->hdr.width;
   while (row) {
     count = *data++;

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

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

As you can see, no consideration was given as to whether any “run count” is in fact greater than the remaining number of pixels in a row. If that happens, integer underflow will occur on the row variable, which will become a very large (2^64 on 64-bit) positive integer. The loop will continue and suffer from buffer overflow on the output canvas and buffer overread on the input buffer. With no obvious way to exit the wild copy loop, exploitation is not favored.

4: FLIC decoder: out-of-bounds read with wild chunk size
In general, the FLIC decoder lacked any defences for reading off the end of the input buffer. Tons of bugs here. But a gstreamer rewrite to avoid raw pointer access and use buffer object APIs for reading and writing seems to have fixed this area reasonably. Bravo.

The most obvious way to demonstrate this was to declare a chunk in the file with a huge size. The input pointer would be incremented by this size and then the next chunk header read from a wild location, leading to a crash.

5: FLIC decoder: integer overflow in output buffer allocation
Looks fixed in the code; untested.

This bug is more interesting than it first appears. The faulty line of code is simple enough:

        out = gst_buffer_new_and_alloc (flxdec->size * 4);

But what type is flxdec->size?

struct _GstFlxDec {
 gsize size;

Where gsize appears to be like a size_t.
So this is one of those interesting cases this is a vulnerability on 32-bit but ok on 64-bit. On 64-bit, the largest value of size was 0xffff * 0xffff. Multiplying again by 4 cannot exceed the width of a 64-bit type. On 32-bit, integer overflow is possible and results in a memory corruption, although a fairly wild one!

6: vmnc decoder: wild read due to integer overflow
Fixed, possibly as an accidental side effect of fixing the more serious integer overflow CESA-2016-0002.

if (type == CURSOR_COLOUR) {
   datalen += rect->width * rect->height * dec->format.bytes_per_pixel * 2;
 if (len < datalen) {
   GST_LOG_OBJECT (dec, "Cursor data too short");

A simple integer overflow in calculating how much input data is required for a cursor of a given size.

Closing notes
Bugs bugs glorious bugs.