Wednesday, December 14, 2016

Chrome OS exploit: one byte overflow and symlinks

The following article is an guest blog post from an external researcher (i.e. cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 author is not a Project Zero or Google researcher).

This post is about a Chrome OS exploit I reported to Chrome VRP in September. The Project Zero folks were nice to let me do a guest post about it, so here goes. The report includes a detailed writeup, so this post will have less detail.

1 byte overflow in a DNS library

In April I found a TCP port listening on localhost in Chrome OS. It was an HTTP proxy built into shill, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Chrome OS network manager. The proxy has now been removed as part of a fix, but its source can still be seen from an old revision: shill/http_proxy.cc. The code is simple and doesn’t seem to contain any obvious exploitable bugs, although it is very liberal in what it accepts as incoming HTTP. It calls into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 c-ares library for resolving DNS. There was a possible one byte overflow in c-ares while building cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DNS packet. Here is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerable code, stripped heavily from its original to make cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug more visible:

ares_create_query.png

It parses dot-separated labels and writes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m into a buffer allocated by malloc(). Each label is prefixed by a length byte and separating dots are omitted. The buffer length calculation is essentially just a strlen(). A dot that follows a label accounts for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length byte. The last label may or may not end with a dot. If it doesn’t, 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 buffer length is incremented in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first black box to account for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length byte of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last label.

Dots may be escaped though and an escaped dot is part of a label instead of being a separator. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last label ends with “\.”, an escaped dot, 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 first black box wrongly concludes that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length byte of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last label has already been accounted for. The buffer remains short by one byte and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 least significant byte of dnsclass overflows. The value of dnsclass is most commonly a constant 1.

Exploit from JavaScript?

Shill runs as root. A direct exploit from JavaScript would accomplish in a single step what might ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise take three: renderer code execution -> browser code execution -> privesc to root. This means less work and fewer points of failure. It’s convenient that shill and chrome are separate processes, so if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit fails and crashes shill, it doesn’t bring down chrome and shill is restarted automatically. The direct exploit turned out to be possible, but with difficulties.

There doesn’t seem to be an obvious way to get chrome to place “\.” at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of a Host header using HTTP. So instead cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit uses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 TURN protocol with WebRTC. It encodes what looks like HTTP into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 username field of TURN. TURN is a binary protocol and it can only be used because HTTP parsing by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 proxy is lax.

Also, shill is listening on a random port. The exploit uses TURN again, to scan cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 localhost ports. It measures connection time to determine if a port was open. The scan also runs into a surprising behavior explained nicely in here. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source and destination TCP ports of a localhost connection attempt happen to match, 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 kernel connects cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 socket to itself. Anything sent on a socket is received on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same socket. This causes false positives, so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 scan must retry until a single port remains.

A more difficult issue is that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re aren’t any decent memory grooming primitives. The proxy allocates cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 headers into a vector of strings. It applies minimal processing to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Via and Host headers, forwards cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 headers to anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r server and frees cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m. It accepts a single client at a time. The number of headers is limited to <= 0x7f, header size is <= 0x800 bytes and TURN packet is <= 0x8000 bytes. The rough plan is to do rooming over 6 connections or stages. The problem is that different stages need to reliably place allocations at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same location. This is difficult because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory layout changes between connections in ways that are hard to predict. The solution is to create what I call a persistent size 0x820 byte hole.

820 hole

First, it should be mentioned that shill uses dlmalloc, which is a best-fit allocator. malloc() uses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 smallest free chunk that can fit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 request. free() coalesces any neighboring free chunks.

Let’s look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 picture of grooming at stage 1. This creates a persistent hole of 0x820 bytes:

groom_820.png

Red means that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 chunk is in use chunk and green means free. Cyan is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 large top chunk of dlmalloc. The number on each chunk is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 chunk size in hex. 0x is omitted. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rest of this post, I’ll always refer to chunk sizes in hex, omitting 0x. Also, I’ll often refer to chunk sizes as nouns, which is a short way of referring to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 chunk with such size. I’ll omit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 actual grooming primitives used for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se allocations, but for those interested, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Host and Via header processing in here is used.

So cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first picture shows how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 820 hole is created. Four chunks of size 410 are allocated from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 top chunk in [0-3]. In [5,6], cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first 410 is freed and replaced with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 backing allocation of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vector of headers. Even though cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 headers cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365mselves are freed after stage 1 connection closes, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 backing allocation of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vector is persistent across connections. The fourth 410 is also freed and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer for incoming server data is placed into it. It is also persistent across stages. Then cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 connection closes, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 two 410 headers in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 middle are freed and consolidated into 820.

Why is this 820 hole useful? It is persistent because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous and following 410 are not freed between stages. Each stage can now start with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 steps:

  • allocate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 820
  • eat all free holes up to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 top chunk by doing tons of small allocations
  • free cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 820

Let’s say a stage cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n allocates a small chunk of 100. dlmalloc uses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 smallest free chunk, which is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 820, because smaller ones were allocated. Now let’s say cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stage finishes and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 100 is freed. Next stage can use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same algorithm to place a 100 at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same location. This capability allows just enough grooming in stage 2 and 3 to get from one byte overwrite to overlapping chunks.

But things could go wrong. There might be anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r 820 hole by chance and different stages might allocate a different 820. Or it could happen that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 tons of small allocations fail to eat all holes, because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 amount of memory allocated per connection is limited. So cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit attempts to get rid of most of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 free chunks before stage 1 by combining different techniques. An interesting one perhaps is that it intentionally crashes shill. The process is restarted automatically and starts with a clean heap layout. It also uses two techniques to allocate lots of memory—more than what’s allowed by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 limits mentioned above. I won’t go into details here though.

Overlapping chunks

Stage 2 triggers cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory corruption and stage 3 creates overlapping chunks:

groom_overlap.png

First, a 1e0 chunk is allocated in [10-12] by allocating 640, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n 1e0 and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n freeing 640. Then cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 query buffer of ares is allocated into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 110 slot at [13]. This leaves a free 530 in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 middle. Now is a good time to take a closer look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dlmalloc chunk header declared here:

malloc_chunk.png

This header is kept in front of each chunk. The 3 least significant bits of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size field are used as flags. Most importantly, lsb = 1 indicates that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous chunk is in use. So looking at [13], cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 530 chunk has size = 531 and 1e0 chunk has prev_size = 530. The prev_size field is only used when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous chunk is free. Ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous chunk spans cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 prev_size field. This means that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size field of 530 immediately follows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 query buffer in 110. The single byte that overflows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 query buffer overwrites cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 least significant byte of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size field of 530: 0x31 -> 0x01. So cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 three flags are not affected. But chunk size is corrupted from 530 to 500 as can be seen from [14].

What’s interesting is that 1e0 doesn’t know anything about this corruption and its prev_size remains 530. Now, [15-17] split cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 free 500 into free 2e0 and in-use 220. But dlmalloc is already confused at this point. When it tries to update cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 prev_size of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 chunk following 220, it’s off by 30 bytes from 1e0. And 1e0 keeps on believing that prev_size = 530. It also believes that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous chunk is free even though 220 is in-use. So now in [18], 1e0 is freed. It tries to coalesce with a previous 530 chunk. There is a 2e0, where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re used to be 530. dlmalloc is fine with that and creates a large 710 chunk that overlaps cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 220.

These kind of overlapping chunks are relatively easy to exploit. They’re good both for breaking ASLR and getting RCE. This technique for going from a single byte overflow to overlapping chunks is not new. Chris Evans demonstrated it here in 2014 as part of an investigation for this Project Zero post. I’m not sure if anyone has demonstrated earlier.

What’s not shown in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 picture for simplicity is that [14-15] is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 boundary between stage 2 and 3. The memory corruption of stage 2 occurs in DNS code after Via and Host headers are processed, so no furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r grooming is possible. Stage 3 continues with grooming to get overlapping chunks. But cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 110 query buffer is actually freed after stage 2. Stage 3 needs to reallocate a 110 chunk at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same location. The method described above is used.

ASLR

Stage 4 breaks ASLR. It first turns cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 overlapping 220 into a more convenient 810 chunk:

groom_810.png

So it allocates cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 820, which overwrites cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 header of 220 and changes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size to 810. It’s interesting to note that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fd and bk pointers in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 header of 220 are also overwritten. The exploit can’t afford to corrupt pointers at this point because it hasn’t broken ASLR. But fd and bk are only used when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 chunk is free—cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are used for a doubly linked freelist. [21] frees cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 overwritten chunk and dlmalloc finds it to be of size 810.

Next, two free 2a0 chunks are crafted into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 810:

groom_aslr.png

So 2a0 is allocated, 2d0 is allocated and 2a0 is freed. Now, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 recently mentioned fd and bk pointers are leaked to break ASLR. The two 2a0 chunks have cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same size and are placed into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same freelist. With additional grooming at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 beginning of stage 4, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit can be certain that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 two chunks are cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 only ones in this freelist. Well, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is also a third element linked in—cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 freelist head allocated statically from libc. So looking at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first 2a0, its fd and bk point to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r 2a0 and into libc. Also, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first 2a0 overlaps with 820, which contains an HTTP header that is forwarded to an attacker-controlled HTTP proxy. So that leaks two pointers that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 proxy server forwards to JavaScript. The two pointers are used to calculate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of 820 and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 base address of libc.

To root

ASLR defeated, stages 5 and 6 get code execution:

groom_system.png

The rough idea is to overwrite a BindState which holds callback information—a function pointer and arguments. The function pointer is overwritten to point to system() in libc, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 base address of which is known. And cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first argument is overwritten to point to a shell command string crafted into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 820 slot, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of which is also known. BindState chunk size is 40, so now, 810 is resized to 40. First, [25] frees 2d0, which consolidates to 810. For cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 810 chunk to be placed into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size 40 freelist, it is removed from its current freelist by allocating it in [27]. 810 size is overwritten to 40 by freeing 820 in [26] and reallocating it with new data in [28]. [29] frees cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 resized 40 and [30] allocates a BindState into it. BindState now conveniently overlaps with 820. [31-32] reallocates 820 to corrupt cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 BindState to launch system(). The particular callback used triggers in 30 seconds and system() runs a shell command as root.

Persistence bug

It may sound surprising, but an attacker that has gained root on Chrome OS will lose cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 privileges after reboot. Chrome OS has verified boot. Bootloader in read-only memory verifies cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel, which in turn verifies cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hash of each disk block that it needs during runtime. This applies to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system partition which contains all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 executable binaries, libraries and scripts. So an attacker can’t just set up a script to run at boot. But cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is also a stateful partition that can be modified. It is intended for variable stuff like logs, configuration files and caches.

The way this exploit achieves persistence across reboots will sound familiar to anyone who’s read about this exploit by geohot. Both use symlinks, dump_vpd_log and modprobe. The dump_vpd_log script itself was fixed to not follow symlinks, but here is a snippet from /etc/init/ui-collect-machine-info.conf:

persistence_bug.png

/var is a stateful partition so UI_MACHINE_INFO_FILE can be turned into an arbitrary symlink. dump_vpd_log --full --stdout writes /mnt/stateful_partition/unencrypted/cache/vpd/full-v2.txt to stdout. This can be used to create an arbitrary file with arbitrary contents during boot. geohot used dump_vpd_log to write a command into /proc/sys/kernel/modprobe at boot so a following modprobe would execute cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 command. But cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are some extra problems when trying to reuse this approach.

The first issue is that /var/run is a symlink to /run, which is a tmpfs and not persistent. The exploit makes /var/run persistent by relinking it to /var/real_run. Some parts of Chrome OS get confused by that and it is dealt with by using more symlinks. I’ll skip cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 details here.

modprobe.d config file

So now it’s possible to write into arbitrary files during boot. Anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r issue is that writing into /proc/sys/kernel/modprobe with dump_vpd_log won’t work in this case, because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following udevadm writes into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same file and its output can’t be controlled. The last write() syscall is what counts when writing into /proc/sys/kernel/modprobe. So instead, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit creates /run/modprobe.d, which is is a configuration file for modprobe. Parsing of modprobe.d is lax. Any line starting with "install modulename command..." specifies a command to execute when that module is loaded. Any lines that fail to parse are ignored.

Late modprobe

The final problem is that ui-collect-machine-info.conf runs late during boot, when all modprobing is complete. The created configuration file is not of much use. So cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 final trick is to find a way to trigger modprobe late during boot. The exploit creates a device file with mknod, which has a major number 173. 173 is unknown to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel, which means that when something accesses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device file, 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 kernel will attempt to modprobe a handler module named char-major-173-0. Then it is sufficient to turn some commonly accessed file into a symlink to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device file and each access to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file will modprobe. The exploit uses /var/lib/metrics/uma-event.

There is yet one more issue. Stateful partitions are mounted with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 nodev flag, which blocks access to device files. So cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device has to be moved to /dev during startup. This code in /etc/init/cryptohomed.conf is used for that:

mv.png

The device is created as /mnt/stateful_partition/home/.shadow/attestation.epb and /mnt/stateful_partition/unencrypted/preserve/attestation.epb is turned into a symlink to /dev/net. This moves cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device to /dev/net. /dev/net is used instead of /dev because cryptohomed changes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 owner of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target attestation.epb. This would change cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 owner of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 whole /dev directory and cause chrome to crash.

So that completes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Rube Goldberg machine of symlinks. dump_vpd_log creates /run/modprobe.d configuration file with a command to launch as root. cryptohomed moves a device file to /dev/net. Any generated metric accesses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 uma-event symlink to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device, which launches modprobe, which launches a command from modprobe.d.

Patches


By now, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 issues have been fixed pretty thoroughly. c-ares was patched in Chrome OS and upstream. The HTTP proxy was removed from shill. TURN implementation was hardened to block JavaScript from sending an arbitrary username to a localhost TCP port. And cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 symlink issues were fixed here, here, here and here.

Thursday, December 1, 2016

BitUnmap: Attacking Android Ashmem

Posted by Gal Beniamini, Project Zero

The law of leaky abstractions states that “all non-trivial abstractions, to some degree, are leaky”. In this blog post we’ll explore cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ashmem shared memory interface provided by Android and see how false assumptions about its internal operation can result in security vulnerabilities affecting core system code.

We’ll walk through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process of discovering and exploiting a vulnerability resulting from this leaky abstraction, which will allow us to elevate our privileges from any Android application to a multitude of privileged contexts, including cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 highly-privileged “system_server”. This vulnerability has been present in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 core Android platform code for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Marshmallow and Nougat versions. It has now been fixed in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 recent Android bulletin. For a detailed disclosure timeline, see cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “Timeline” section below.

One Device to Bind Them


As you know, Android applications can perform inter-process communication (for example, in order to communicate with various Android services), by using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Android binder. Initially, each Android service registers itself with a central daemon; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “service manager”. Subsequently, applications may contact cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 daemon in order to request “handles” with which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 registered services may be contacted.

In keeping with good IPC design, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 interface provided by binder itself is racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r simplified. In fact, binder transactions are capped to a size of at-most 1MB. Well cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n, what about scenarios in which we need to transfer a large amount of data to a system service? For example, what if we want to modify cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 phone’s wallpaper? What about playing a media file? Surely we wouldn’t need to “re-invent” a mechanism through which memory can be shared in larger quantities between processes using binder transactions.

Indeed, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s no need to worry. You see, binder transactions support cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 transfer of more than just binary data. In fact, binder transactions can be used to transfer file descriptors and even ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r binder handles. For instance, this is how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “service manager” is able to provide processes with handles through which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y may communicate with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 requested services.

binder.png

Great, so we have a means of communicating with ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r processes - even in large volumes. But what about cases where we’d like to share (racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than simply transfer) large quantities of memory? Once again - no need to worry! For this purpose, Android has introduced a means of sharing memory, called “ashmem” (Android Shared Memory).

Sharing (memory) is Caring


So what exactly is ashmem? In short, each ashmem file descriptor acts as a handle to a shared memory region. The device also allows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user to perform several memory-sharing operations via a set of supported ioctls. These allow cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user to set cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shared memory region referred to by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ashmem file descriptor, modify cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 name of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shared region and even control cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 protection mask with which this descriptor may be mmap-ed.

Let’s take a closer look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 actual implementation of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ashmem device, starting with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ioctl used to control cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size of a shared memory region - ASHMEM_SET_SIZE:

static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
   struct ashmem_area *asma = file->private_data;
   long ret = -ENOTTY;
   switch (cmd) {
       

     ...
   }
   return ret;
}

As we can see above, this ioctl simply allows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user to pass in any size. As long as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shared memory region has not been mapped yet, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device will happily record cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 passed-in size as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying size corresponding to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory region. Not only that, but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 recorded size can cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n be queried by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user by issuing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 corresponding ioctl - ASHMEM_GET_SIZE.

So… this looks racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r suspicious. What exactly does “setting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size” actually mean? Recall that ashmem regions are mapped-in by calling mmap, and, as we know, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mmap syscall receives an argument denoting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mapping to be created.

This begs cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 question: what if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size of a created mapping does not match cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size of an ashmem region? Perhaps cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementation of mmap will simply ignore cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 passed in size argument and use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size provided in ASHMEM_SET_SIZE? Perhaps, instead, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementation of mmap will simply ignore cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying size and use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size argument instead? There’s only one way to find out:

static int ashmem_mmap(struct file *file, struct vm_area_struct *vma)
{
   struct ashmem_area *asma = file->private_data;
    ...
   if (!asma->file) {
       ...
       vmfile = shmem_file_setup(name, asma->size, vma->vm_flags);
       ...
       asma->file = vmfile;
   }
   ...
   vma->vm_file = asma->file;
   mutex_unlock(&ashmem_mutex);
   return ret;
}

As we can see above, while cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 actual shared memory file is created using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shared memory region (using “shmem_file_setup”), cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory mapping itself is created using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 virtual memory area (vma) - that is, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size passed in to mmap.

Let’s take a step back and reflect on this decision.

Essentially, this means that developers who attempt to use an ashmem region must take special care to always call mmap using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same size of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying shared memory region. Failing to do so will not return any visible error code to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 developer, but will instead create a mapping with potentially dangerous consequences.

For starters, if a developer mmap-s a region with a size larger than cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 actual underlying region’s size, any attempt to access memory beyond 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 underlying region will result in a illegal access, sending cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SIGBUS signal to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process (which will cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n probably subsequently terminate).

But cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r, more interesting, potential pitfall. What if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 developer erroneously assumes that size of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying shared memory region and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mmap-ed size must be equal to one anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r? To try and answer this question, I’ve audited all instances of Android services which use ashmem to share memory with a user. From here on, we’ll focus on one such case - Bitmaps.

Mismatching Assumptions


As we mentioned earlier, one operation which requires cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 transfer of potentially large quantities of memory from one process to anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exchange of images. For this purpose, Android exposes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Bitmap class, which can be easily serialized into a binder parcel.

As Bitmaps may be larger than cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 1MB limit for binder transactions, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 image’s data must be transferred via ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r means - such as ashmem. Indeed, for bitmaps above a certain size, this is exactly how this data is transferred. We can see that by looking at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “unflattening” function, “Bitmap_createFromParcel”:


   android::Parcel* p = android::parcelForJavaObject(env, parcel);
   const SkColorType colorType = (SkColorType)p->readInt32();
   const SkAlphaType alphaType = (SkAlphaType)p->readInt32();
   const int width = p->readInt32();
   const int height = p->readInt32();
   const int rowBytes = p->readInt32();
   …
   std::unique_ptr<SkBitmap> bitmap(new SkBitmap);
   if (!bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType), rowBytes)) {
       return NULL;
   }
   …
   …
}

First, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 image’s metadata is extracted from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 parcel and is used to construct an SkBitmap instance to hold this information. Next, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function proceeds to reads cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 actual bitmap’s data by calling Parcel::readBlob.

However, this looks potentially dangerous! First of all, note that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function uses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SkBitmap instance (i.e., cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 one holding all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 metadata we read earlier on) in order to calculate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “size” of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 resulting bitmap’s data. Then, it calls Parcel::readBlob in order to actually read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 transferred data, while using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previously calculated size as an input argument.

Finally, let’s take a look at how Parcel::readBlob actually reads cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 enclosed Bitmap’s data:

   …
   int fd = readFileDescriptor();
   ...
   void* ptr = ::mmap(NULL, len,
                                   isMutable ? PROT_READ | PROT_WRITE : PROT_READ,
                                   MAP_SHARED, fd, 0);
   ...
   return NO_ERROR;
}

Aha! This is exactly what we were looking for. Instead of using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying size of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory region, Parcel::readBlob performs an mmap operation using our own controlled length argument (i.e., cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size computed from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bitmap’s metadata).

In and of itself, this is already a bad practice; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user could pass in an ashmem descriptor with any arbitrary size, resulting in an “invalid” memory mapping of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 type we discussed earlier. This, in turn, would trigger a SIGBUS if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 receiving application attempts to access cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mmap-ed memory region.

But perhaps we can do better than just creating an invalid mapping? We’ve seen how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory is mapped-in when creating a Bitmap, but how is it unmapped?

Well, after reading in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bitmap’s data, Bitmap_createFromParcel proceeds to store cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 information about cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mapped data in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 constructed Bitmap instance:

Bitmap::Bitmap(void* address, int fd,
                           const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
                               : mPixelStorageType(PixelStorageType::Ashmem) {
   mPixelStorage.ashmem.address = address;
   mPixelStorage.ashmem.fd = fd;
   mPixelStorage.ashmem.size = ashmem_get_size_region(fd);
   mPixelRef.reset(new WrappedPixelRef(this, address, info, rowBytes, ctable));
   // Note: this will trigger a call to onStrongRefDestroyed(), but
   // we want cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pixel ref to have a ref count of 0 at this point
   mPixelRef->unref();
}

However, instead of using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previously created mapping, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 constructor simply retrieves cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mapping using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ASHMEM_GET_SIZE ioctl (using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thin-wrapper ashmem_get_size_region). This is, once more, a false assumption - namely, that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying ashmem size and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mmap-ed region must be equal to one anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r. Finally, once cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bitmap is freed, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 area is unmapped by calling:

void Bitmap::doFreePixels() {
   switch (mPixelStorageType) {
       ...
       case PixelStorageType::Ashmem:
           munmap(mPixelStorage.ashmem.address, mPixelStorage.ashmem.size);
           close(mPixelStorage.ashmem.fd);
           break;
       ...
   }
}

Putting it all togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r, this means that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mmap and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 munmap calls are performed with potentially different length arguments, both of which are fully controllable by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker:

  • The length of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mmap operation is calculated from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bitmap’s metadata
  • The length of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 munmap operation is calculated from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ashmem’s size

The mismatch between cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mmap-ed and munmap-ed length provides us with a great exploitation primitive! Specifically, we could supply a short length for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mmap operation and a longer length for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 munmap operation - thus resulting in deletion of an arbitrarily large range of virtual memory following our bitmap object. Moreover, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s no need for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 deleted range to contain one continuous memory mapping, since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 range supplied in munmap simply ignores unmapped pages.

Once we delete a range of memory, we can cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n attempt to “re-capture” that memory region with controlled data, by causing anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r allocation in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remote process. By doing so, we can forcibly “free” a data structure and replace its contents with our own chosen data -- effectively forcing a use-after-free condition.

From here on, we’ll refer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se crafted bitmaps with mismatching lengths as “BitUnmap”s.

bitunmap.png

Thinking of an Exploit Strategy

Finding a Target Service


Okay - now that we finally have a good exploitation primitive, how can we use it in order to write a stable exploit that will allow us to gain code execution in system_server? First of all, we’ll need to find a binder call in system_server which un-flattens a Bitmap object (or unparcels a Bundle containing a Bitmap) from a binder transaction. Moreover, it would be advantageous if we were able to access this call from any context, requiring no permissions.

Luckily, system_server houses many different binder services, increasing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 odds of finding a comfortably exploitable endpoint.

One such candidate is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “notification” service, which provides an interface for interaction with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 notification bar. Among cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 operations supported by this service, any application (even those running in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 isolated_app SELinux context and those requiring no permissions whatsoever) may attempt to add a notification object which is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n handled by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 notification service. Moreover, Notification objects are complex, and may contain images (bitmaps) along with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 presented text.

Notification.png

Using notifications is also advantageous for a completely different reason - cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir lifetime is controlled by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker. Remember that after we free a memory range in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remote process, we’d like to re-capture that memory region with our own controlled data. Well, as we’ve already seen, Bitmap objects are mmap-ed into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remote address-space, and contain completely controlled data. What’s more, once a notification is added to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 notification bar, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Notification object will remain referenced in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remote process (until cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 notification is removed). This effectively means we can use notifications for a dual purpose:

  • Sending crafted BitUnmaps to delete memory ranges in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remote process
  • Sending regular Bitmaps with controlled data in order to reclaim freed regions

Finding a Target Data Structure


After we’ve established our ability to unmap arbitrarily large memory regions in system_server by crafting our own Notification objects containing BitUnmap instances, we still need to think of a method with which we’ll be able to reliably hijack control flow in system_server. That is, we need to decide which data structure to unmap using our primitive.

First, consider cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fact that our BitUnmap objects are allocated by calling mmap. Due to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 behaviour of mmap, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y will simply inhabit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 highest memory address between mm->mmap_base and TASK_SIZE which contains a sufficiently large contiguous hole. This means that, for example, attacking single C++ or Java objects would be racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r difficult, as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are located in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap and are surrounded by ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r data structures which we would have to “repair”.

Moreover, even if we were able to hijack such data structures, we would still need to craft cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir content in a way which would allow us to eventually hijack cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 control flow. For example, exploiting use-after-free vulnerabilities in C++ objects normally includes hijacking cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir vtable and replacing it with a structure pointing to ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r executable memory locations. With modern exploit mitigation techniques, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 amount of steps required to bypass XN-bit protection and ASLR could be quite substantial.

Perhaps cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s a cleaner way to get code execution?

Optimally, we’re looking to replace a data structure that has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following properties:

  • Is allocated directly using mmap (i.e., not “on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap”)
    • Therefore we don’t need to “repair” adjacent data structures
  • Allows direct control over code execution
    • Saves us cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 need to craft a complex chain to bypass mitigation techniques
  • Does not require address-space dependant information unique to system_server
    • Saves us cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 need to find (or create) an information leak to bypass ASLR

In order to find this magical structure, I’ve written a small gdb script to monitor and log all calls to mmap coupled with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir origins and sizes. This helped me group cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “families” of mmap-ed objects which could be useful for exploitation. After producing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 list, one such candidate stood out as having all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 above properties - Threads.

Hanging by a Thread


As we know, all threads in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same process share cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same address space. Moreover, system_server, being cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 large process that it is, contains many code paths which handle long-running or periodic tasks. Naturally, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se code paths are handled by creating additional threads in system_server. When threads are created, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y require two data structures which are unique to each thread: cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread metadata (pthread_internal_t) and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread’s stack.

Looking at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code in Android’s bionic, we can see that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se two regions are, in fact, carved from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same memory region, which is allocated using mmap:

                                              pthread_internal_t** threadp,
                                              void** child_stack) {
   if (attr->stack_base == NULL) {
       // The caller didn't provide a stack, so allocate one.
       // Make sure cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack size and guard size are multiples of PAGE_SIZE.
       mmap_size = BIONIC_ALIGN(attr->stack_size + sizeof(pthread_internal_t),    
                                                        PAGE_SIZE);
       attr->guard_size = BIONIC_ALIGN(attr->guard_size, PAGE_SIZE);
       //Calls mmap to create cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mapped space, and mprotect to create a guard page
       attr->stack_base = __create_thread_mapped_space(mmap_size, attr->guard_size);
       …
       stack_top = reinterpret_cast<uint8_t*>(attr->stack_base) + mmap_size;
       …
   }
   ...
   pthread_internal_t* thread = reinterpret_cast<pthread_internal_t*>(stack_top);
   ...
}

So, if we are able to deallocate a thread’s stack and immediately reclaim it as our own, we should be able to directly achieve code execution simply by virtue of already having prepared a stack which performs whichever operation we’d like to execute within system_server.

Regardless, before we can start scheming about hijacking a thread, we need to think of a way to spawn a thread in system_server on-demand.

Going through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 available binder calls terminating in system_server reveals one such comfortably accessible code-path: AudioService::loadSoundEffects. This binder command can be called without requiring any permissions, and causes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 AudioService to load a predefined set of sound effects in its internal “sound pool”. Creating such a pool causes system_server to spawn several threads:

soundpool.png

In this exploit we’ll hijack cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “SoundPoolThread”, for a variety of reasons:

  1. It is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first thread created when creating a new sound pool. This fact will come in handy later, when we try and force it to be allocated in a controlled memory region.
  2. After initializing a sound pool, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “SoundPoolThread” awaits furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r commands by waiting on a condition variable. Until cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 condition variable is signaled, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “futex” system-call will not return. As cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread will not access its stack while it is waiting for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system-call to return, this allows us to replace cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread’s stack with our own, leaving cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread none cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 wiser. Once we’ve performed our switch, we can trigger a chain of events (such as closing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sound pool by calling unloadSoundEffects) which will signal cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 condition variable and cause cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system-call to return.
  3. Once cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sound pool is closed, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SoundPoolThread is no longer needed and simply terminates anyway. This means we can safely hijack it without causing any adverse effects in system_server.

A Short ROP Chain


Once we hijack cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SoundPoolThread’s stack, we’ll need to replace it with our own ROP stack. So, what should we run cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re? Well, for versions of Android prior to 7.0, we could simply write a short ROP chain which mmap-s one of our ashmem file descriptors with executable access-permissions, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n jumps directly to it. Indeed, this would allow us to simply place executable code at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 base of an ashmem descriptor and send it along to system_server.

However, as of Android 7.0, this solution would no longer work. This is because starting from Android 7.0, a new set of mitigations have begun to roll out which aim to prevent unauthorised code execution within system_server (and ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r privileged processes). A small subset of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se rules can be seen here:

# system_server should never use JIT functionality
neverallow system_server self:process execmem;
neverallow system_server ashmem_device:chr_file execute;
neverallow system_server system_server_tmpfs:file execute;

Specifically, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second rule prevents ashmem file descriptors from being mapped as executable within system_server. Moreover, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 use of execmem (in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first rule) prevents system_server from mapping “new” executable memory regions within cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process.

So… we could still write a large ROP chain to perform whichever set of commands we’d like within system_server. Due to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 breadth of system_server, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ROP gadgets could conceivably even be turing-complete. However, this seems incredibly complex to do manually, and quite time consuming to automate.

Instead, we could look for a way to directly bypass cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mitigation mentioned above. We’ll do so by looking for SELinux contexts which can be executed within system_server. After a short search, we stumble upon cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following SELinux rule:

# system_server should never execute anything from /data except for /data/dalvik-cache files.
neverallow system_server {
   data_file_type
   -dalvikcache_data_file #mapping with PROT_EXEC
}:file no_x_file_perms;

Okay, so files with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SELinux context dalvikcache_data_file may be freely mapped as executable. This makes sense as when Android applications are installed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y go through an “optimization” process, resulting in new files which may be executed more quickly. As system_server loads many of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se optimized files, it needs to be able to map cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m in as executable to use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m.

While system_server may use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se files, it cannot directly create cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m. In fact, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 only process which is allowed to create files with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se contexts is installd - cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 installer daemon.

The distinction, however, is purely semantic. This is due to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fact that system_server can directly issue commands to installd using a special socket. One such command can be issued to cause installd to start such an optimization process (resulting in a file with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dalvikcache_data_file context) from any chosen file and to any chosen destination. Thus, all our ROP chain would need to do is to open cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 installd socket, issue this command to create our wanted executable chunk, and finally map that new file as executable.

We could even go one step furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r - since we are launching our attack from an Android application, we could simply embed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 executable shellcode within cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source code of our application (for example, within a static byte array). Once our application is installed, it will be optimized, resulting in a new file of type dalvikcache_data_file). As luck would have it, this optimization process does not garble static byte arrays, meaning our shellcode will now reside in this new executable file. Finally, all we need to do is to simply locate this optimised file on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 disk and map it in as executable.

dalvikcache.png

Lastly, when crafting our ROP stack we’ll need to know where to start writing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 gadgets relative to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack’s base address. That is, we need to know cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 accurate position in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack at which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack pointer will be placed when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SoundPoolThread enters cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 command loop.

Luckily, we can avoid cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 need to “guess” 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 stack (and place a ROP-slide) by using additional trick; we can simply start up such a thread in our own process (by creating a sound pool object), and wait for it to enter cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 command loop. Once cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread settles, we can read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 /proc/$pid/stat for that task and measure 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 “SP” token against cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 base address of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack for that thread.

Bypassing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 need to Bypass ASLR


Up to now we’ve been avoiding cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 question of ASLR. This has been no mistake, but racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r a happy accident. In order to allow for fast application startup times, Android applications are forked from a single process, appropriately named zygote. This process preloads many commonly used shared libraries and resources, thus saving applications cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 need to do so on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir own when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are launched.

Among cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 processes forked from zygote is our target for exploitation - system_server.

This essentially means that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 portion of our address space which are inherited from zygote are shared with system_server, including a multitude of shared libraries which are available at our disposal.

As such, once we manage to hijack a running thread, we can create an entire valid ROP stack using gadget addresses located in our own address space (so long as we stick only to zygote-loaded shared libraries). Doing so allows us to ignore ASLR for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 purpose of this exploit, as all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r stages of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit are address-independent anyway.

zygote_aslr.png

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


Lastly, before we can put togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r a complete exploit, we still lack cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to shape cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remote address space. Specifically, we need to reach a state that will cause cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 targeted objects that we would like to delete to be placed in controlled memory regions, which could cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n be freed and subsequently reclaimed.

Recall that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 behaviour of mmap dictates that for every allocation, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 chosen memory address will always be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 highest address in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 range of mm->mmap_base to TASK_SIZE which contains a sufficiently large contiguous unmapped region.

This makes mmap an optimal allocator to use when attempting to shape cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address space - most ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r allocators require special conditions in order to revert to contiguous allocations.

In our case, we already know that we would like to replace cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory region reserved for a thread’s stack. We’ll denote cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size of this region by “thread_size”.

First, we’ll send over a large amount of notification objects to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remote service, each of which containing a bitmap of size thread_size. This will allow us to fill in any “holes” in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remote address space which may have been opened up during cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lifetime of system_server. After doing so, we are guaranteed that any subsequent allocation of size thread_size will be contiguous and placed 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 current mmap-ed region.

shape_fill.png
Next, we allocate many consecutive bitmap objects of size thread_size, each of which referenced by a single Notification object. After doing so, we intentionally remove a single notification from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 consecutively allocated set. Then, we proceed bombard cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system_server process with many small binder transactions triggering a remote garbage collection to occur. This, in turn, frees up cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 space previously held by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bitmap object in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 removed notification, creating a thread_size sized hole, like so:

hole.png

Now, we can re-load cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 audio effects, causing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SoundPoolThread to be spawned in system_server. As this is a thread_size-d allocation and all previous holes of that size have already been filled (by our bitmap objects), it will have to populate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vacant hole that we just created amidst our chunk of contiguous bitmaps.

Now, we can remove cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 notification directly in front of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SoundPoolThread’s stack and once again trigger a remote garbage collection. This will cause anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r hole to open up, bringing us to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following state:

hole2.png

Once we’ve strategically opened up a hole in front of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SoundPoolThread, we can send our crafted BitUnmap object. We’ll create it so that its metadata size (i.e., cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size used in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mmap) will be equal to thread_size. However, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ashmem descriptor will actually be twice that size! This means cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 once our BitUnmap is freed in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remote process, it will unmap itself, along with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SoundPoolThread.

unmap.png

Finally, now that we’ve freed up cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SoundPoolThread’s stack, we can send one more notification - this time of size 2*thread_size, which will fill in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last created hole, effectively replacing both cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 region held by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 BitUnmap, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 region containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SoundPoolThread’s stack.

remap.png

Putting it All Togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r


At long last, we have all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 steps needed to create a complete working exploit. Here’s a short run-down of all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 steps we discussed, which allow us to hijack a thread within cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system_server process using our own crafted ROP stack:

  1. Build a ROP stack using gadgets in zygote-originating shared libraries
  2. Unload all sound effects (closing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current SoundPoolThread, if present)
  3. Allocate many bitmaps of size thread_size to force consecutive allocations
  4. Open up a hole in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 middle of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 consecutively allocated bitmaps
  5. Reload cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sound effects to create a new SoundPoolThread within cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 created hole
  6. Open up a new hole directly in front of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous hole
  7. Send a BitUnmap object to fit in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 newly created hole
  8. Force a remote GC to cause cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 BitUnmap to free itself and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SoundPoolThread
  9. Allocate a new bitmap to occupy cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 space previously held by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SoundPoolThread
  10. Unload all sound effects - causing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 condition variable to be signalled, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365reby triggering cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SoundPoolThread to start executing using our own crafted stack!

Afterword


Although this blog post is quite lengthy, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are many smaller details that I neglected to mention here (in favour of some brevity). I’d encourage anyone who’s interested to dig into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 full source code of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit to discover any such “missing” pieces.

Along with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source code for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit itself, I’m also releasing a project which can be used to easily create shellcode for use in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit itself. Specifically, this project contains a small assembly stub which fixed up cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 freed pthread_internal_t structure 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 stack and corrects cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread-local storage AARCH64 registers to point to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir new location. After performing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 needed fixups, it simply jumps to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shellcode’s main function.

In order to allow for easy shellcode creation in a high-level language (such as C) racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than hand-coding it all in assembly, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shellcode wrapper uses a custom linker script to prepend cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 small fixup stub to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shellcode, and compiles cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shellcode itself as position independent.

You can find cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source code for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shellcoder here.

Timeline


  • 07.09.2016 - Vulnerability reported
  • 07.09.2016 - Initial response from Android security, assigned Android-ID
  • 23.09.2016 - Exploit submitted to Android security
  • 26.09.2016 - Android notify that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fix will be present in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 next partner bulletin
  • 27.09.2016 - CVE-2016-6707 assigned
  • 01.11.2016 - Vulnerability fixed and released in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 November bulletin