As part of our platform research in Zimperium zLabs, we recently disclosed a buffer overflow vulnerability affecting multiple Android DRM services to Google. Google classified it as high-severity, designated it as CVE-2017-13253 and have patched it in the March security update.
In this blog post, we’ll cover the details of the vulnerability. First, we’ll go over relevant background information, from general Android mechanisms to specific mechanisms related to the vulnerability. We’ll focus on the recently introduced Project Treble and what its changes actually mean. Then, we’ll analyze the vulnerability and its impact. We’ll look into how due to other faults on some devices this vulnerability could be exploited in order to achieve root privileges. Lastly, we’ll talk about the origin of the vulnerability and how it could have been prevented. We’ll see how despite Google’s claim that Project Treble benefits security, it’s possible that it did the opposite.
Note that in this post I aim to cover a lot of background information related to this vulnerability. If you already have experience with Binder and libbinder you might want to skip the first section and start with the information more specific to the vulnerability itself.
Android’s Binder & Security
A very common security model used in many operating systems (including Android) revolves around inter-process communication (IPC). Untrusted code running in an unprivileged process can communicate with privileged processes (services) and ask them to perform very specific actions that the OS allows. This model relies on the services (and the IPC mechanism itself) to properly validate every input sent from the unprivileged process. In turn, this means that bugs in those services, especially in the input validation part, can easily lead to vulnerabilities.
For example, recent iOS vulnerabilities found by Rani Idan right here at Zimperium rely on this method. Bugs in IPC input validation allow an attacker to execute code with higher permissions from an unprivileged app.
In Android’s case, the IPC mechanism is called Binder. The security of Android Binder services is then naturally very interesting for vulnerability research. Binder has many useful features, for example, it allows processes to transfer complex objects between each other, like file descriptors or references to other Binder services. In order to maintain simplicity and good performance, Binder limits each transaction to a maximum size of 1MB. In cases where processes need to transfer large data, they can use shared memory to quickly share the data between them.
Binder’s C++ library
Android’s Binder library (libbinder) gives many abstractions for C++ code which relies on Binder. It allows you to call methods of remote instances of C++ classes almost as if they don’t reside in another process.
Each object which uses this mechanism implements a few classes in a predefined structure:
- An interface class which defines the methods of the object which should be callable through Binder. Prefixed with “I”.
- A “client-side” class in charge of serializing the input and deserializing the output. Prefixed with “Bp”.
- A “server-side” class in charge of deserializing the input and serializing the output. Prefixed with “Bn”.
Eventually, when using the object the interface type is almost always used. This allows you to treat the object in the same way whether it’s in the same process or in a different one.
Example of libbinder’s usage in the ICrypto interface
The “server-side” part of the code traditionally lies inside the privileged service (although in some cases the roles are reversed), so it is usually in charge of validating the input. Validation code can begin at the Bn* class and continue along the subsequently called methods. This is obviously the most interesting part for vulnerability research.
The ICrypto interface and the decrypt method
After covering Binder in general, let’s take a look at the specific implementation relevant to the vulnerability. The mediadrmserver service (which is, unsurprisingly, in charge of DRM media) provides an interface to a Crypto object, the interface is then named ICrypto. Note that the object was recently changed to CryptoHal, we’ll go into that later. The general purpose of this interface is to allow unprivileged apps to decrypt DRM data which requires higher privileges to decrypt, like access to the TEE. The details of the encryption itself are not in the scope of this blog post, as again, we’re more interested in the input validation.
ICrypto has multiple methods, but undoubtedly the most important one (which all the others revolve around) is decrypt.
One of the first noticeable things in decrypt’s signature is how complex the input is. From our point of view, this is very interesting. Complex input leads to complex validation code (every parameter is transferred over Binder and has to be verified) which can be susceptible to vulnerabilities.
To read the original article: