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