(All the vulnerabilities have been responsibly disclosed and fixed. I will post the CVE IDs and timelines in the following posts.)
What's the Goal?
Transcendence. From Android, that is.
This is going to be a series of blog posts detailing a chain of vulnerabilities that I've discovered which will enable us to escalate our privileges from any user up to the highest privilege of all - executing our code within TrustZone itself.
Since I only have my personal Android device, a Nexus 5 powered by a Snapdragon 800 SoC, I will focus on the TrustZone platform present on my device - Qualcomm's TrustZone implementation.
It should be noted that Qualcomm's TrustZone platform is present on all devices powered by Qualcomm SoCs, however, they also allow OEMs to make modifications and additions to this platform, which I will go into in more detail in later blog posts.
Also, I believe objectively Qualcomm's TrustZone implementation is a good target since the Snapdragon SoCs are quite ubiquitous and can be found in a very wide range of devices (which isn't surprising, considering Qualcomm has a very large market share in the smartphone chipset market).
Android & Security
Over the years many security mechanisms have been added to Android, and existing ones have been improved.
While the underlying security architecture hasn't changed, the defences have become quite formidable on modern devices, to the point where gaining high privileges can become quite a difficult task, many times requiring more than a single vulnerability.
If you haven't already, I recommend that you read Google's "Android Security Overview", which explains the security architecture and lists most of the security mechanisms which are currently in use.
(For the rest of these blog posts, I'm going to assume that you are at least somewhat familiar with Android's security architecture).
What is TrustZone?
(First, an obligatory TrustZone schematic from ARM Ltd.)
"...a system-wide approach to security for a wide array of client and server computing platforms, including handsets, tablets, wearable devices and enterprise systems. Applications enabled by the technology are extremely varied but include payment protection technology, digital rights management, BYOD, and a host of secured enterprise solutions."
In short, this means TrustZone is a system which is meant to enable "secure execution" on a target device.
In order to execute secure TrustZone code, a specific processor is designated. This processor can execute both non-secure code (in the "Normal World") and secure code (in the "Secure World"). All other processors are limited to the "Normal World" only.
TrustZone is used for various purposes on Android devices, for example:
- Verifying kernel integrity (TIMA)
- Using the Hardware Credential Storage (used by "keystore", "dm-verity")
- Secure Element Emulation for Mobile Payments
- Implementing and managing Secure Boot
- DRM (e.g. PlayReady)
- Accessing platform hardware features (e.g. hardware entropy)
Peripherals are able to access the state of these bits and therefore can deduce whether or not we are currently running in the secure world or not.
How does TrustZone's security model work?
ARM also has a short technical overview of how TrustZone's Secure Model works, which is worth a read.
To achieve secure execution, the boundary between TrustZone and non-TrustZone code must be defined. This is achieved by defining two "worlds" - "Secure World" (TrustZone) and "Normal World" (in our case, Android).
As you know, when in the "Normal World" there is a security boundary between code running in "User-mode" and code running in "Supervisor-mode" (Kernel-mode).
The distinction between the different modes is managed by the Current Program Status Register (CPSR):
The five mode bits (marked by "M" in the image above), control the current execution mode. In the case of the Linux kernel, User Mode (b10000) is used for regular user code, and Supervisor Mode (b10011) is used for kernel code.
And yet, there's something missing here - there's no bit to indicate what is the currently active "world". That is because there is a separate register used for that - the Secure Configuration Register (SCR):
As with the CPSR register, the "Normal World" cannot modify the SCR register directly. It can, however, execute an SMC opcode, which is the equivalent of a SWI for regular supervisor mode calls. SMC is short for Supervisor Mode Call, and is the opcode which can be used to issue requests directly to the TrustZone kernel.
Also, it should be noted that the SMC opcode can only be called from a supervisor context, which means that regular user code cannot use the SMC opcode.
In order to actually call TrustZone related functionality, the supervisor code, in our case, the Linux kernel, must register some sort of service which can be used to call the relevant SMC calls when needed.
In the case of Qualcomm, this is achieved by a device driver called "qseecom" - short for Qualcomm Secure Execution Environment Communication. We'll talk more about this driver in the later blog posts, so hang tight.
Putting it all together
So the road ahead is pretty long - in order to get to TrustZone code execution from a user-mode Android application with no permissions, we'll need the following privilege escalation vulnerabilities:
- Escalation from an Android application with no permissions to a privileged Android user.
- Escalation from a privileged Android user to code execution in the Linux kernel.
- Escalation from the Linux kernel to code execution in the TrustZone kernel.
In the next blog post, I'll cover more details about Qualcomm's TrustZone implementation, and the vulnerability I discovered and exploited within its kernel.