28/03/2015

Getting arbitrary code execution in TrustZone's kernel from any context

(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.)

According to ARM Ltd., TrustZone is:

"...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)

In order to secure the whole system, and not just the application processor, specific bits on the system bus are set when entering "Secure World" and unset when returning to the "Normal World".

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?

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 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):

This register is a co-processor register, in CP15 c1, which means it can be accessed using the MRC/MCR opcodes.

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.

So if this seems like it might interest you, keep reading!

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.

Yes - interrupts *can* be a source into TrustZone, and in fact are in many cases. For example, on Qualcomm devices there are peripherals in charge of segregating different memory areas to different processors; when a violation occurs, the TrustZone kernel catches and handles the interrupt triggered, etc.

As for period tasks - this is also true. For example, there is a watchdog process which checks that other processors on the SoC are still "alive". This is implemented by a periodic interrupt which is handled by the TrustZone kernel causing it to check that the processors have written a "heartbeat" to a shared memory location.