Thursday, November 24, 2016

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

Overview
Recently (Nov 21st, 2016), I published an 0day exploit against cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 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 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 immediate issue but also some similar bugs in ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r functions in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 decoder. More on those ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r bugs in anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r post. Here is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 git commit.

The response from Ubuntu, one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 distributions I use, was also fast. On Nov 22nd 2016, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y published a gstreamer advisory: USN-3135-1. At cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 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, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fix was not 100% correct. Presented here is an 0day PoC (PoC, not exploit!) for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remaining memory corruption issue, along with a description of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code error.

The remaining vulnerability
Bounds checks were added for all three of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 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 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 skip pixel count plus cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of pixels to write stays within cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bounds of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 canvas width. But cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s subtle problem: cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 format permits multiple skip and count pairs per canvas line. And cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 skip counts are considered individually racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than cumulatively. Therefore, it’s possible to get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 skip + count check to pass while still writing off cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 line. If we arrange to write off cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 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 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 line will be a write off cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last line and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore a memory corruption.

And cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 main line of bytes inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 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 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 line, and writes 0 pixels (FF 00). This stays within bounds for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 checks. The second packet skips 0 bytes furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r along (00) and writes 128 bytes (80) of value ‘A’ (41). The second packet passes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 check because 0 + 128 is less than cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 width of 255. But that check didn’t take into account cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fact that our first packet skipped 255 bytes along cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 line. So an out of bounds write results.

In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 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 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original one, but with less “range” in terms of how far forward into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 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 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PoC from here: crash_delta_fli_2.flx.

Closing notes

Happy Thanksgiving!

Monday, November 21, 2016

[0day] [exploit] Advancing exploitation: a scriptless 0day exploit against Linux desktops

Overview
A powerful heap corruption vulnerability exists in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 gstreamer decoder for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 FLIC file format. Presented here is an 0day exploit for this vulnerability.
This decoder is generally present in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 default install of modern Linux desktops, including Ubuntu 16.04 and Fedora 24. Gstreamer classifies its decoders as “good”, “bad” or “ugly”. Despite being quite buggy, and not being a format at all necessary on a modern desktop, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 FLIC decoder is classified as “good”, almost guaranteeing its presence in default Linux installs.

Thanks to solid ASLR / DEP protections on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 (some) modern 64-bit Linux installs, and some ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r challenges, this vulnerability is a real beast to exploit.

Most modern exploits defeat protections such as ASLR and DEP by using some form of scripting to manipulate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 environment and make dynamic decisions and calculations to move cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit forward. In a browser, that script is JavaScript (or ActionScript etc.) When attacking a kernel from userspace, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “script” is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userspace program. When attacking a TCP stack remotely, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “script” is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 program running on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker’s computer. In my previous full gstreamer exploit against cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NSF decoder, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 script was an embedded 6502 machine code program.

But in order to attack cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 FLIC decoder, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re simply isn’t any scripting opportunity. The attacker gets, once, to submit a bunch of scriptless bytes into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 decoder, and try and gain code execution without furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r interaction...

… and good luck with that! Welcome to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 world of scriptless exploitation in an ASLR environment. Let’s give it our best shot.

Obligatory screenshot, and downloads
fedora_flx_exploit.png

In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 above screenshot, we see cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit file opened using xdg-open from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 terminal (which does cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same thing as a user clicking on a browser download). The exploit file opens in rhythmbox on Fedora, which is shown, as well as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 resultant calculator. The terminal output shows an amusing side effect of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit, which we’ll cover later.

The exploit is running against a default install of Fedora 24, except that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 gstreamer packages were upgraded to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 latest: dnf update gstreamer* (v1.8.3-1.fc24.x86_64). We could have targeted eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r totem or rhythmbox as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 binary to exploit, simply by renaming cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file extension of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit. totem tends to handle video extensions and rhythmbox audio extensions. We chose .flac for rhythmbox.

This vulnerability applies equally as well to Ubuntu 16.04, and probably anything else with gstreamer installed. To get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit to work on anything ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exact Fedora versions noted above, though, you’d need to fiddle with a large number of heap and code offsets in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit.

The astute reader will wonder if this exploit might be a full super serious drive by download exploit when paired with my previous Google Chrome and Fedora tracker notes from last week. The answer is sure. If you reworked this exploit to work in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 unsandboxed /usr/libexec/tracker-extract process, by fiddling with a few heap offsets etc., you’d have an unpatched drive by download exploit against Chrome + Fedora.

You can download cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit as demonstrated above from here, or download a fairly minimal crash file to check for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerability here.

Ubuntu vs. Fedora again
Back in 2014, I sandbagged by exploiting Fedora in preference to Ubuntu because Fedora was easier (which we fixed). In this instance, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exact opposite is true: I decided to exploit Fedora because it is much harder. Ubuntu, even in 16.04, has some problems:

  • Missing ASLR on many binaries, including security sensitive ones:
    file /usr/lib/rhythmbox/rhythmbox-metadata
    /usr/lib/rhythmbox/rhythmbox-metadata: ELF 64-bit LSB executable, x86-64
  • Generally little use of RELRO. The Fedora exploit was complicated by having to work around RELRO.

In particular, going after this exploit on Ubuntu would have been much much faster due to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 missing ASLR. But cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 point here is to go after a genuinely scriptless exploit in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 presence of solid ASLR. So we choose Fedora as our exploitation target.

The vulnerability
We find cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerable decoder code in gst-plugins-good/gst/flx/gstflxdec.c, function flx_decode_delta_fli():

flx_decode_delta_fli (GstFlxDec * flxdec, guchar * data, guchar * dest)
{
...
 /* use last frame for delta */
 memcpy (dest, flxdec->delta_data, flxdec->size);

 start_line = (data[0] + (data[1] << 8));
 lines = (data[2] + (data[3] << 8));
 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 */
     dest += *data++;

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

     if (count > 0x7f) {
...
     } else {
       /* replicate run */
       while (count--)
         *dest++ = *data++;

The above function is called via a FLX_LC command in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input file. At cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 time of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 call, dest points to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 start of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 output canvas buffer, e.g. 8 x 8 pixels and always 1 byte per pixel. data points to attacker controlled data from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 raw input file.

Unfortunately, it doesn’t take much to see that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s a complete lack of bounds checking here against cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 canvas width and height. To get an out-of-bounds write, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker simply has to specify a start_line value greater than cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of lines in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 output canvas (bug 1). Or cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y could specify a skip count that goes past cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last line of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 output canvas (bug 2). Or cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y could specify a write count that goes past cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last line of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 output buffer (bug 3) (applies both to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 literal run and replicate run code paths).

(Absent a CVE, you can uniquely identify this cluster of issues CESA-2016-0004.)

The constraints and challenges
We’ve identified a very powerful heap overflow primitive, with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following properties:

  • Attacker controls arbitrary number of bytes written, each with an arbitrary value.
  • Non-linear overflow: attacker can “skip over” a bunch of heap to target a precision destination.
  • Attacker controls size of allocation that is overflowed out of, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365reby offering some opportunities to control where in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap it goes.

However, we still need to know what to write out of bounds, which is a challenge without script. Absent any nice piece of data to corrupt, such as a string passed to system(), or a JIT code buffer, we’re going to need to start messing with pointers. And due to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 good ASLR, we can’t directly syncá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365size a valid pointer value.

In order to try and start gaining a bit of control over cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploitation environment, we turn to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rhythmbox media player. This is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 default player on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 desktop for various audio formats. The rhythmbox / gstreamer combination has some very intriguing (and useful!) properties:

  • No-one really cares about cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file extension. So if you put a video file inside a file with an audio file extension, it’ll get processed according to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 content and not cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file extension.
  • The metadata for a media file is fully scanned, even if it is a video file and not an audio file.
  • rhythmbox does its metadata scanning in a new, fresh subprocess -- rhythmbox-metadata. This results in very clean and predictable heap layouts. Also, if our exploit messes up and crashes, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 parent rhythmbox process and UI are largely unaffected.
  • gstreamer decoders typically start off in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir own fresh thread, which gets its own fresh thread heap under cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 modern Linux glibc allocator.

We’ve got enough to fight through without worrying about heap layout, so using rhythmbox to get us a passably deterministic heap setup is a great start.

Unfortunately, using rhythmbox-metadata carries one huge challenge: because it is just extracting metadata from a media file, it only runs cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 decode loop for just 2 frames. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 amount of work we can do per frame turns out to be limited (and it does!), we have very little opportunity to complete an exploit, or rewire cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 decode loop to run longer.

The exploit: primitives used
We decide to base our exploit around corrupting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 metadata object for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 actual decoder itself, which is defined thus:

struct _GstFlxDec {
 GstElement element;
 GstPad *sinkpad,*srcpad;
 gboolean active, new_meta;
 guint8 *delta_data, *frame_data;
 GstAdapter *adapter;
 gulong size;
 GstFlxDecState state;
 gint64 frame_time;
 gint64 next_time;
 gint64 duration;
 FlxColorSpaceConverter *converter;
 FlxHeader hdr;
};

This decoder object is typically referenced through a pointer flxdec in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code. Because of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 strong heap consistency described above, we’ll typically find a constant offset between cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 flxdec->frame_data buffer which we are corrupting off cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 flxdec object itself. This means that we can use a fixed value of start_line in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerable flx_decode_delta_fli() function above, and target corruption of specific fields of this object, such as flxdec->converter. Of course, for all this to work, we need cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 metadata object to be after flxdec->frame_data in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap. The heap layout is reasonably deterministic for reasons already covered, and different heap layouts can be achieved by changing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input size canvas, or cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file extension (which seems to trigger vastly different logic and code paths inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file format auto detection).

Trick #1: corrupting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 converter field to upgrade to an absolute write primitive
The initial corruption primitive we have is a relative write off cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of an existing heap buffer. On 64-bit, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 range is certainly limited such that it is not possible to write earlier in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap, and writing later in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap is also subject to some constraints. Given that an exploit needs to deference fairly arbitrary pointers, we’ll need to upgrade our write primitive. Getting an absolute write primitive is great but we should note we need some form of ASLR defeat before cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 absolute write becomes fully powerful.
If we corrupt flxdec->converter, we can subsequently use a FLX_COLOR256 command in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input file. This effectively calls flx_decode_color(), which writes attacker supplied bytes (in multiples of three) to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address flxdec->converter + 8.

Trick #2: awesomely useful 3-byte partial pointer overwrite within thread heap arena
On modern glibc, malloc() on a thread will return heap chunks from a per-thread heap arena. This heap arena has very strong alignment of 64MB on 64-bit. A typical thread heap start address might be 0x7fffa8000000. This strong alignment is very useful for partial pointer overwrites. Fully 3 bytes can be overwritten without having to worry about differing alignments. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 flxdec->converter pointer is partially overwritten so that it points early in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread heap arena to corrupt a function pointer.

Trick #3: single byte partial pointer overwrite of a function pointer
In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s a function pointer at thread heap arena offset 0x002818, gst_list_iterator_resync(). Linux ASLR places code with page level granularity, and this particular pointer value ends in 0x6f0 within a page. Determinism can be retained by corrupting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 single least significant byte, but no more. As is happens, at offset 0x6e8 (accessible via a single byte corruption), cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code:

 xor  %eax,%eax
 retq

We use that little gadget to force cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 decoder loop to continue indefinitely, earlier in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit.

Trick #4: locating a copy primitive
Being able to perform partial pointer overwrites only is no fun because it will mostly limit us to exploring and corrupting within thread heap arenas. What we’d really like is a copy primitive so that we can read a pointer value and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n write it somewhere out of bounds. After a bit of code study, we do see such a primitive in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 decode loop. First, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 read:

flx_decode_delta_fli (GstFlxDec * flxdec, guchar * data, guchar * dest)
{
...
 /* use last frame for delta */
 memcpy (dest, flxdec->delta_data, flxdec->size);

We can corrupt flxdec->delta_data (eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r a full overwrite, or a partial pointer overwrite) and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore read from various interesting memory locations. flx_decode_delta_fli() can be called as many times as we want per frame, via multiple FLX_LC commands. Results of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 read are placed in flxdec->frame_data.

And cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 write:

gst_flxdec_chain() {
        /* decode chunks */
         flx_decode_chunks (flxdec,
             ((FlxFrameType *) chunk)->chunks,
             chunk + FlxFrameTypeSize, flxdec->frame_data);

         /* save copy of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current frame for possible delta. */
         memcpy (flxdec->delta_data, flxdec->frame_data, flxdec->size);

This memcpy() is a write of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 content we previously read into flxdec->frame_data. This content is written to flxdec->delta_data, anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r pointer we can corrupt to point to where we want. Unfortunately, this write only fires once per frame, at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end, and as we covered earlier, we have a frame budget of 2! Togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r, this read and write are a decent copy primitive. We should be able to use it to chase pointer chains.

Trick #5: corrupting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input file buffer
This trick is pretty neat as is enables us to do more inside our budget of 2 frames. We can use our copy primitive to copy a bunch of pointers and write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input file. We can cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r corrupt cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input file buffer, in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 spaces in between cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointers we just copied, to create commands that effectively write real pointer values to useful locations.
We also use this trick in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit, to restore flxdec->delta_data. After corrupting it to chase a chain of pointers, we need to put it back, ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise we’ve lost control of it via partial pointer overwrites. In order to put it back, we write write its original value into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input file buffer.

Trick #6: co-opting an addition primitive
This is my favorite trick used! By far! While cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to copy pointers around cleanly is very useful, it is rarely going to be sufficient. For example, when building a ROP chain, we’d typically leak a pointer into a code section, such as a function pointer, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n calculate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of useful opcode sequences based on addition to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 leaked pointer. Or, if we want to mess with a GOT entry, it typically isn’t a simple pointer copy, but a read / add / write sequence.
So how can we get an addition primitive going without script?
It turns out that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 decoder maintains time from frame to frame, and it does it like this, continuing on from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 gst_flxdec_chain() code quoted above:

         /* save copy of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current frame for possible delta. */
         memcpy (flxdec->delta_data, flxdec->frame_data, flxdec->size);

         gst_buffer_map (out, &map, GST_MAP_WRITE);
         /* convert current frame. */
         flx_colorspace_convert (flxdec->converter, flxdec->frame_data,
             map.data);
         gst_buffer_unmap (out, &map);

         GST_BUFFER_TIMESTAMP (out) = flxdec->next_time;
         flxdec->next_time += flxdec->frame_time;

This is super cool: we can co-opt that frame time calculation to instead be an addition of a constant of our choosing to a pointer. We just have to copy a pointer into flxdec->frame_time and write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 addition constant to flxdec->next_time. When cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 next frame starts, a new calculated pointer will be present in flxdec->next_time and we can again use our copy primitive to put it somewhere useful.

The exploit: frame-by-frame
With detailed study into primitives available, we’re now in a position to link a bunch of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r to get an exploit going, no script required ;-) We’ll break things down into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 actions taken each frame. If things look a little bit busy in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first couple of frames, don’t be surprised. Remember, we have just 2 frames to do something drastic to cause cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 decoder to continue into furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r frames.

Frame 1
  • Use FLX_LC to do a 3 byte partial pointer overwrite on flxdec->converter; point it to offset 0x280e into thread heap arena.
  • Use FLX_COLOR256 to do a 1 byte partial pointer overwrite on a gst_list_iterator_resync() pointer; point it to a gadget that does “return 0”.
  • FLX_LC; point flxdec->converter to offset 0x2830.
  • FLX_COLOR256; 3 byte partial pointer overwrite of existing pointer into thread heap arena, make it point to offset 0x24100, which is a GstPad object.
  • FLX_LC; point flxdec->converter to offset 0x2840.
  • FLX_COLOR256; 3 byte partial pointer overwrite of existing pointer into thread heap arena, make it point to offset 0x24100, which is a GstPad object.
  • FLX_LC; point flxdec->converter to offset 0x2824.
  • FLX_COLOR256; write 12 bytes of FLIC protocol in between pointers we just corrupted, to form a chunk of input file.
  • FLX_LC; point flxdec->converter to offset 0x2837.
  • FLX_COLOR256; write 9 bytes of FLIC protocol in between pointers we just corrupted, to form a chunk of input file.
  • FLX_LC; point flxdec->converter to offset 0x3a118. This is actually inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 flxdec object and is guaranteed to be a string of 8 zeros. This is necessary to prevent cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 converter from being used (and crashing) at end of frame.
  • FLX_LC; 3 byte partial pointer overwrite to point flxdec->delta_data to offset 0x2818.
  • FLX_LC; no-op to set flxdec->size to 56, but it is already 56. This is a relic from an earlier version of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit.
  • FLX_LC; point flxdec->delta_data to offset 0x3cb68. This is inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input file buffer.
  • Allow frame tick.
  • Copy primitive is invoked: cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last FLX_LC reads 56 bytes at thread heap arena offset 0x2818 into flxdec->frame_data. And cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 frame tick copies those 56 bytes from flxdec->frame_data to flxdec->delta_data, which points to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input buffer. We just copied a chunk of program, with 3 embedded pointer values, to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input buffer.

Frame 2
  • FLX_LC; point flxdec->converter to offset 0x240f6, which is just before a GstPad object that we want to corrupt. This object controls whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r decoding continues or not.
  • FLX_COLOR256; no-op, just to get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 alignment of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input file right.
  • FLX_COLOR256; copy a few bits to corrupt cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GstPad, including copying cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “return 0” function pointer gadget on top of GstPad::chainfunc. This value is not in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original input file, and only placed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input buffer corruption in frame 1 above.
  • FLX_COLOR256; copy a few more bits to corrupt cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GstPad, including replacing GstPad::peer and GstPad::parent with pointers to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GstPad itself. Again, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se values are not in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original input file, and only placed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input buffer corruption in frame 1 above.
  • FLX_LC; set flxdec->size to 8. Maybe superfluous.
  • FLX_LC; set flxdec->converter to offset 0x24128, which is inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GstPad object we are busy corrupting and faking.
  • FLX_COLOR256; write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value 0, this effectively sets GstPad::flags to 0.
  • FLX_LC; set flxdec->converter to offset 0x241c8, which is inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GstPad object.
  • FLX_COLOR256; write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value 1, this effectively set GstPad::mode to GST_PAD_MODE_PUSH.
  • FLX_LC; set flxdec->converter to offset 0x242d0, which is inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GstPad object.
  • FLX_COLOR256; write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value 0, this effectively sets GstPad::num_probes to 0.
  • FLX_LC; set flxdec->converter to offset 0x3a118, which is a safe place for frame tick as per frame 1.
  • FLX_LC; set flxdec->delta_data to offset 0x24128, which is a read of GstPad::parent (which we set to point to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GstPad itself, at offset 0x24100).
  • FLX_LC; set flxdec->delta_data to offset 0x3a110, which is flxdec->srcpad.
  • Allow frame tick, which copies cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GstPad pointer into flxdec->srcpad.

Right here is a critical time in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit. We need to have corrupted enough of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 gstreamer decoder state to prevent cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 decode loop from stopping. The key code line to cause continuation is here in gstflxdec.c:

        res = gst_pad_push (flxdec->srcpad, out);
We need this to return GST_FLOW_OK (0). It succeeds because we’ve pointed flxdec->srcpad to a thoroughly hacked up GstPad, which streamlines code flow through gst_pad_push(), to return 0 as easily as possible. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end it wasn’t that easy -- various pointers are chased even during cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 simplest code path, and a function pointer is called, even when you clear all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 flags and special status variables. We eventually win cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “return 0” when flxdec->srcpad->peer->chainfunc is called, which we set to our special gadget we created with a partial function pointer overwrite.

Frame 3
Things are a bit more sane now. We can do a simpler amount of work per frame tick without having to worry about running out of frame ticks. We can use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 copy / addition primitive available at frame tick as many times as necessary.
  • FLX_LC; set flxdec->name to offset 0x3a148, which now points to flxdec->frame_time. flxdec->name is not special in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code, it’s just a convenient place to build and store a derived pointer value for later use.
  • FLX_LC; set flxdec->delta_data to offset 0x3a020, which points to flxdec->name.
  • FLX_LC; set flxdec->delta_data to offset 0x3cf4a, which is inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input file buffer.
  • Frame tick: copies from flxdec->name, which contains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value &flxdec->frame_time, to a point in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input file buffer; we’ll need it later.

Frame 4
  • FLX_LC; set flxdec->next_time to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 8 byte value 0x140, which is a constant we want our addition engine to add.
  • FLX_LC; set flxdec->delta_data to offset 0x3a108, which will cause a read of flxdec->sinkpad.
  • FLX_LC; set flxdec->delta_data to offset 0x3a148, which is &flxdec->frame_time.
  • Frame tick: copies flxdec->sinkpad to flxdec->frame_time, which is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n added to flxdec->next_time, leaving flxdec->sinkpad + 0x140 in flxdec->next_time.

Frame 5
  • FLX_LC; set flxdec->delta_data to offset 0x3a150, which is a read of flxdec->next_time, or our calculated pointer value.
  • FLX_LC; set flxdec->delta_data to offset 0x3c8b0, which is a saving of our calculated pointer value toward cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 beginning of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input file buffer. Also useful for debugging.
  • Frame tick, already explained.

Frame 6
  • FLX_LC; read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 saved calculated pointer value at offset 0x3c8b0.
  • FLX_LC; write it to offset 0x3a120, which is flxdec->delta_data.
  • Frame tick, already explained.

Frame 7
  • FLX_LC; reads from flxdec->delta_data which currently points to flxdec->sinkpad + 0x140. This reads cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of a gst_flxdec_chain() function pointer.
  • FLX_COLOR256; writes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer value that we wrote into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input file buffer in frame 3 above, which is &flxdec->frame_time. Writes it to flxdec->delta_data, restoring it. Until we restored it, it pointed outside of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread heap arena, so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 partial pointer overwrites that we’ve been using were broken until we just restored it.
  • Frame tick: writes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 gst_flxdec_chain() function pointer to flxdec->frame_time.

Frame 8
  • FLX_LC; writes 8 byte pointer offset value 0x202f70 to flxdec->next_time.
  • Frame tick: addition! Leaves gst_flxdec_chain() + 0x202f70 in flxdec->next_time. This is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 read only address of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memcpy() GOT entry.

Frame 9
  • FLX_LC; points flxdec->delta_data to &flxdec->next_time, for read of calculation result.
  • FLX_LC; points flxdec->delta_data to offset 0x3c8c0, a place in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 in input file buffer, to write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 calculation result at frame tick time.
  • Frame tick, already explained.

Frame 10
  • FLX_LC; set flxdec->name to offset 0x3a148, which now points to flxdec->frame_time. (Can’t convince myself this is still necessary -- I don’t think cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value changed since we set it to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same value in frame 3).
  • FLX_LC; set flxdec->delta_data to offset 0x3a020, which points to flxdec->name.
  • FLX_LC; set flxdec->delta_data to offset 0x3d1ca, which is inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input file buffer.
  • Frame tick: copies from flxdec->name, which contains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value &flxdec->frame_time, to a point in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input file buffer; we’ll need it later.

Frame 11
  • FLX_LC; point flxdec->delta_data to read from offset 0x3c8c0, which is where we stashed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GOT address of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memcpy() function pointer in frame 9.
  • FLX_LC; point flxdec->delta_data to write to flxdec->delta_data at frame tick. Writes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GOT address of memcpy() to flxdec->delta_data.
  • Frame tick, already explained.

Frame 12
  • FLX_LC; reads from flxdec->delta_data, effectively reading cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memcpy() function pointer value into flxdec->frame_data.
  • FLX_COLOR256; writes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer value that we wrote into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input file buffer in frame 10 above, which is &flxdec->frame_time. Writes it to flxdec->delta_data, restoring it.
  • Frame tick: writes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memcpy() function pointer to flxdec->frame_time.

Frame 13
  • FLX_LC; writes 8 byte pointer offset value 0xffffffffffef91c0  to flxdec->next_time.
  • Frame tick: addition! Leaves memcpy() + 0xffffffffffef91c0 in flxdec->next_time. This is effectively a subtraction, calculating cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of system().

Frame 14
  • Abbreviating a bit harder now. Read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result in flxdec->next_time and write it to offset 0x3c8d0, which is a useful storage location in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input file buffer.

Frame 15
  • Read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stored system() value at offset 0x3c8d0, and write it to flxdec->frame_time.

Frame 16
  • Write pointer offset value 0x37b840 into flxdec->next_time, and use frame tick to add it to system(). The result is 8 bytes before __free_hook in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 glibc BSS. (We use 8 bytes before because we’re going to do a write through flxdec->converter, which writes at an offset of positive 8 bytes.)

Frame 17
  • Read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 resulting value from flxdec->next_time and stash it safely away at offset 0x3c8e0, a useful storage location in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input file buffer.

Frame 18
  • Read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stashed __free_hook - 8 address value at offset 0x3c8e0, and write it at offset 0x3d60a, which is furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r forward into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input file buffer.

Frame 19
  • Read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stashed system() value at offset 0x3c8d0, and write it at offset 0x3d62a, which is furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r forward into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input file buffer.

Frame 20
  • FLX_LC; point flxdec->converter to offset 0x39808, which is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 local pointer chunk in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 decode loop.
  • FLX_COLOR256; write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 string “gnome-calculator”.
  • FLX_LX; point flxdec->converter to &flxdec->converter - 8.
  • FLX_COLOR256; write over flxdec->converter. The value written is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer value written into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input file buffer in frame 18, which is __free_hook - 8. The next FLX_COLOR256 will cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore write over __free_hook.
  • FLX_COLOR256; write system() on top of __free_hook. We get system() from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input file buffer, where we wrote it in frame 19.

At this point, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 decode loop will run free(chunk), causing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 free hook to be called. Since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 free hook is system(), and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 contents of chunk is gnome-calculator, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n we get a calculator. Win! As a side effect, tons of calls are made to system() as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rest of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process frees memory. Sometimes, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are side effects as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shell proceeds to interpret cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 content of heap chunks :-)

Closing notes
This was a fairly ridiculous exploit. But it was worth doing because it’s proof that scriptless exploits are possible, even within cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context of decent 64-bit ASLR. It was possible to commandeer memory reads, writes and even additions within cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 decoder loop to slowly but surely advance cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit and gain control.

There are definitely some specific lessons to learn regarding Linux desktop security:

  • Ubuntu has problems with missing defenses such as ASLR, RELRO, etc., even in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 latest 16.04 LTS release.
  • The elevation of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 FLIC format decoder into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 gstreamer “good” plugins set was probably a mistake.
  • More generally, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 partitioning of gstreamer decoders into “good”, “bad” and “ugly” makes sense on some levels, but not for security. For security, a partition of “useful” vs. “obscure” might make more sense. The obscure codecs only provide risk -- I’d recommend even removing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 automatic UI (accessed by e.g. totem) for installing obscure codecs, because it’s not as if standard users need cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m unless cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are under attack.
  • The final exploit primitive was cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 corruption of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 __free_hook glibc variable. We had to go after a variable like this because on Fedora, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GOT function pointers are read only, and… we don’t like ROP, right? :-) It’s worth noting that ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r critical function pointers are protected within glibc. For example, both cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 atexit() and tls_dtor_list function pointers are protected by xor’ing with a “secret”. By cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same underlying reasoning, __free_hook is probably attackable enough that it should be similarly protected. The value of doing so against an arbitrary read / write primitive is however debateable.
  • Code that automatically indexes or thumbnails media files really needs to be sandboxed in this day and age.