Tuesday, September 23, 2014

Exploiting CVE-2014-0556 in Flash

Posted by Chris Evans, Kidnapper of RIP

A couple of weeks ago, Adobe released security bulletin APSB14-21, including 8 fixes for bugs reported by Project Zero. Full details of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se bugs are now public in our bug tracker. Some of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 more interesting ones are a double free in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 RTMP protocol, or an integer overflow concatenating strings. Again, we’d like to thank Adobe for a response time well ahead of our standard 90-day disclosure deadline.


Prelude
Before we get started, though, it’s worth briefly noting why cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is so much value in writing an exploit. Finding and eliminating bugs obviously improves software correctness, but writing exploits is always a significant learning opportunity. Throughout cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 history of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 security industry, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s a long track record of offense driving defense, leading to technologies such as stack canaries, NX support in processors and ASLR.

Project Zero is not just a bug hunting initiative. We’re doing our part to continue cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 tradition of practical and public research of exploitation techniques -- and deriving defenses from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m. For example, our glibc defensive patch was accepted as a follow-on from our glibc exploit.

The case of this particular exploit starts with some irony on account of my overly hasty initial triage of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug based on instincts which were later proved wrong by a more in-depth analysis of exploitation opportunities. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug history, you can see cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 claim “almost certainly 64-bit only” (wrong!) and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n “does not work in Chrome 64-bit Linux”. We learned not to declare anything as unexploitable in our previous post about exploiting a subtle condition in glibc. Therefore, I had to declare shenanigans on myself and tackle cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 challenge: exploit this bug on Chrome 64-bit Linux.

The bug
The bug is triggered by calling BitmapData.copyPixelsToByteArray() with a reference to a ByteArray that has its position property set very large -- close to 2^32. This results in an integer overflow in 32-bit arithmetic. This occurs even on 64-bit because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 relevant positions and length variables are (quite reasonably) stored in 32-bit variables. The code cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n believes that it can copy cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pixels, starting to write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m at position, and stay 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 buffer. Instead, a buffer overflow occurs. On 32-bit, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 out-of-bounds write will be written before 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 buffer because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer will wrap. On 64-bit, things are not as kind to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker. On a typical 64-bit Linux process setup with a 1MB buffer, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 situation will look like this:

… | buffer: 1MB | heap, libs, binary |                 !!

The out-of-bounds write (in red) is at approximately buffer + 4GB. This will not wrap around cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 massive 64-bit address space, leading to a write way off in unmapped space. Insta-crash. The most obvious way to avoid cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 crash is to make cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer massive, almost 4GB, leading to this situation:

… | buffer: 4GB                                      | !! heap, libs, binary |

This is readily exploitable. However, 64-bit Chrome on Linux has a defensive measure where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 amount of mapped address space is limited to 4GB. So cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 large buffer allocation will fail and prevent that particular attack.

The heap groom
We’re going to need a trick to exploit this without slamming into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 4GB address space limit. The breakthrough -- that did not occur to me before attempting to develop an exploit -- comes when we realize that we don’t need to have cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address space contiguously mapped. The out-of-bounds write will happily still go ahead even if it “jumps over” a hole in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address space. By having a hole in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address space, perhaps we can usefully trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 corruption with less than 4GB mapped.

But how do we put this hole where we want it? Looking at how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Flash allocator works using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 strace system tool, we see that very large allocations are serviced using unhinted mmap(). The Linux standard algorithm for servicing unhinted mmap() calls is to stack cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m adjacent and downwards in address space, as long as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re isn’t a hole that can satisfy cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 request. So let’s see what happens when we allocate two 1GB chunks:

… | buffer2: 1GB | buffer1: 1GB | heap, libs, binary |

And cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 free cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first one (a direct munmap() call is seen):

… | buffer2: 1GB |   1GB hole   | heap, libs, binary |

And cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n allocate a 2GB buffer (too big to fit in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hole):

… | buffer3: 2GB        | buffer2: 1GB |   1GB hole   | !! heap, libs, binary |

Aha! We’ve managed to engineer a situation where we’ve never had more than 4GB of address space mapped at any given moment, and at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end, a corruption at buffer3 + 4GB will land right in a writable region: cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap.

The corruption target
Now that we have a reasonably controlled memory corruption situation, we need to pick something to corrupt. As is pretty standard in modern heap buffer overflow exploitation in a scripting environment, we’re going to try and clobber a length of an array-like object. If we clobber any such length to be larger, we will cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n be able to read and write arbitrary relative heap memory. Once we’ve achieved such a powerful primitive, it’s essentially game over. Successful exploitation is pretty much assured: defeat ASLR by reading cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of a vtable and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n write a new vtable that causes execution redirection to a sequence of opcodes that we choose.

We decide to corrupt a Vector. buffer object. This is a fairly standard, documented technique. I recommend Haifei Li’s excellent paper as background reading. Corrupting this buffer object is an obvious target because of three properties it possesses:

  • The attacker can choose arbitrary sizes for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se objects, meaning cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is a lot of control over where in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are placed relative to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pending heap corruption.
  • The object starts with a length field, and corrupting it results in arbitrary heap relative read/write being exposed to script.
  • The object is resilient to corruption in general. Aside from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length field, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is just a single pointer and trashing this pointer does not affect cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Vector, or ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise cause noticeable stability issues during cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 course of exploitation. (We could even restore its value post-exploitation if we wished.)

To proceed, we simply create many (32) Vector. objects, all with buffers sized at about 2MB. These typically end up being stacked downwards at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 top of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 1GB hole. In reality, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 1GB and 2GB allocations end up being a little larger than expected under cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 covers. This means that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 corruption address of buffer3 + 4GB actually ends up corrupting objects within cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 1GB hole instead of after it. This is ideal because we can make sure that only our large buffers are corrupted. In terms of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 actual data to write, we just use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 default values in an empty BitmapData, which are 0xffffffff (white pixels with a full alpha channel). 0xffffffff is a plenty large enough length to proceed with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit!

Proceeding onwards
There is nothing particularly exciting or unique about how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit proceeds to demonstrate code execution, so we’ll skip cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lengthy explanation here. I’ve made an attempt to fully comment cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit source code, so if you want to continue to follow along I recommend you read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 materials attached to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 public bug.

The only part I’d flag as mildly interesting -- because it differs from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previously quoted paper -- is how we get known data at a known heap address. We do it with a Vector. object again. Each of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se is in fact a pair of objects: a script object, which is a fixed sized and contains metadata; and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer object which contains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 arbitrary data prefixed by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length. The script object forms a distinct pattern in memory and also contains a pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer object. By locating any Vector. script object, we can cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n use a raw memory edit to change a property of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object. This property change will be visible to ActionScript so we cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n know which handle corresponds to a buffer at which raw address.

Conclusions, and turning what we’ve learned into generic defenses
Various technologies would have changed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploitation landscape here, and can now be investigated in more detail:

  • Randomized placement of large memory chunks. Non-deterministic placement of large allocations would have broken cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap grooming aspect of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit.
  • Isolation of Vector. buffers. As we’ve seen, corruption of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se buffers is an extremely dangerous condition. Some of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most recent advances in memory corruption defenses have been “isolated” or “partitioned” heaps. These technologies seem applicable here. (They would need to be applied not just to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Vector buffers, but to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 general case: partitioning off read/write objects where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker controls both cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 content.)

Given cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 open-source nature of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ActionScript engine, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 open-source nature of some potentially helpful technologies, a prototype of a generic defense is now on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Project Zero TODO list!