Android Trusted Execution Environment Security Research (3): Privilege Elevation

0x00 Overview

In the first two articles of the series, we first introduced Samsung's TEE OS TEEGRIS, and then described how to implement exploits and gain runtime control against trusted applications (TAs) .

In the final article we will take a closer look at how to gain runtime control over the TEE core .

We will show how the kernel exposes drivers that can be used by privileged TAs to map physical memory into the TA memory space .

We will use the driver in HDCP TA to map security registers and cancel the protection mechanism of TEE memory .

Finally, we will use the same HDCP TA to modify the hypervisor page tables and allow Android applications to map (already unprotected) TEE memory with full read/write access.

0x01 Looking for vulnerabilities

Previously, the function of directly mapping physical memory to TA has been a frequently used vulnerability in TEE exploits. For S7 models, the problem is that the kernel does not perform enough checks on the physical memory areas to be mapped into TA, allowing sensitive locations such as EL3 code to be mapped into TA memory.

It makes sense for us to delve into how TEEGRIS is implemented and check if similar vulnerabilities exist. We quickly found a driver running in memory, accessible via phys://.

pays handler registration:
The phys driver is very similar to the /dev/memLinux device and can be accessed through a number of system calls, the most noteworthy of which are open and mmap. The open system call handler for phys:// does the following.

TA permission check in open system call handler:

In lines 9-13, the code checks whether the TA has the right to map at least one of five potential areas, which are internal RAM, internal boot ROM, secure DRAM, non-secure DRAM, and registers. If you have permission, it will execute successfully.

In the first article, we learned that permissions are statically assigned to each group. Fortunately, the HDCP TA we can implement runtime control belongs to the highly privileged samsung_drv group, which has access to the phys driver. In fact, the TA requires the phys driver to execute one of the commands, so the HDCT TA naturally belongs to the samsung_drv group.

Now that we know TA has access to the application, we can analyze the mmap handler and see what checks are performed.

phys mmap handler:
On line 16, the function get_memory_type is called, passing the provided range as a parameter. Subsequently, the value 1 is shifted left to the return value of the get_memory_type function. In the rest of the code, we see that the function checks whether the result is 8 (corresponding to non-secure RAM, shown in green in the figure), 16 (corresponding to registers, shown in red in the figure), 1 (corresponding to ROM, shown in red in the figure) shown in blue). It appears that secure RAM and internal RAM (running EL3 code) are never mapped into TA, even though they are both in the list of permissions TA can have.

We will not discuss the get_memory_type function in too much detail here. Since the samsung_drv group does not have permission to map ROM, it seems that only non-secure RAM and registers can be mapped in the HDCP TA. Of these two areas, we are less concerned about non-secure RAM, which can be abused by privilege escalation in REE. We are more interested in the TEE because it is entirely located in secure RAM. From the attacker's perspective, more attention may be paid to the registers, because they contain all registers used to configure peripheral devices, including the registers used to configure TrustZone.

Now that we know what to access, we can create a Return Oriented Programming payload that allows mapping and accessing these memory regions. To do this, we chose to create a universal physical memory to physical memory copy payload, specifically performing the following operations:

  • (1) Open the handle of the phys driver;
  • (2) Call mmap twice to map the source buffer and target buffer;
  • (3) Jump to memcpy.

Using the above primitives, we can choose to set the memcpy source to register memory and the target to non-secure memory to read the register; or we can also set the memcpy source to non-secure memory and set the target to register to read the register on Android Implement modifications in the application.

After implementing the ROP Payload, we need to consider what content to copy and where to copy it.

0x02 ION buffer

As a first step, we had to find a way to share physical memory between the CA running on Android and the vulnerable HDCP TA running on the TEE. This process is not trivial because typically, a regular Android application does not know the physical address to which the buffer is mapped.

To this end, we use the ION mechanism provided by Android, which can be understood as a shared memory manager that deletes copies in memory when data is shared between two entities. We can access these buffers in the Android application, so we can use them in the client application. The allocator will guarantee that memory will be mapped to a known physical address. This subsystem can be accessed by interacting with /dev/ion, which allows an open call to be made to obtain a file descriptor and then request an ION buffer via an ioctl.

We downloaded the Android source code that matched the firmware version of a Samsung device and found an ION buffer that met the requirements by viewing the Device Tree Sources. The following file fragment named exynos9820-beyond0lte_common.dtsi contains some details about this buffer, such as the starting address (0x99000000) and size (0xE400000):

camera {
compatible = "exynos9820-ion";
ion,heapname = "camera_heap";
reg = <0x0 0x99000000 0xE400000>;
ion,recyclable;
};

This means that if we map the entire camera_heap ION buffer in CA and map physical address 0x99000000 in HDCP TA, then we should have two views of the same memory.

0x03 Modify TZASC

Having solved this problem, the only problem we have left is finding the register to map and the location to map it to. In the first articles, we mentioned two registers commonly used to configure TrustZone: TZASC and TZPC. If the TA can access any of them, it can read the contents of the register and write to it at the same time, thereby unprotecting the memory area or peripheral device. In principle, modifying either of the two can allow REE to access the TEE, ultimately affecting the original security of the TEE. In our experience, however, targeting TZASC is easier, as the structure of TZPC tends to be device-dependent (there is currently no publicly available documentation for the S10 Exynos chip), but TZASC is already standardized to some extent.

By reverse engineering the TEEGRIS kernel code, we found functions related to TZASC processing, and found that this SoC actually has 4 TZASC peripherals, presumably one for each DRAM controller.

We first search for the string tzasc and then see if there is a match in the TEE kernel binary. The picture below is how we reach the initialization function, which gives us the address of the TZASC handler.

TZASC driver initialization:

The dev://tzasc device can be accessed using the ioctl system call, which ultimately calls tzasc_get_settings_for_range.

Decompiled tzasc_get_settings_for_range:

Although this function does not seem to be very relevant to the attack we are trying to achieve, we noticed that it references a structure called tzasc_physical_addresses, which contains the physical addresses of what we suspect are 4 different TZASC peripherals.

Physical addresses of the 4 TZASC peripherals:
To verify our hypothesis, we correlated this information with the physical memory map of each physical device collected from Android by reading /proc/iomem. The idea behind this verification is that the physical range of the TEE should not overlap with the physical range of the REE. The following list shows that there is a gap between the REE mcinfo register locations, which leaves room for the TZASC register. Note that the TZASC string is added manually in order to read the output, it is not in the normal iomem output.

beyond0:/data/local/tmp # cat /proc/iomem
…
1bc3004c-1bc3004f : /mcinfo@1BC300000
-----TZASC1:0x1BC40000-----
1bd3004c-1bd3004f : /mcinfo@1BC300000
-----TZASC2:0x1BD40000-----
1be3004c-1be3004f : /mcinfo@1BC300000
-----TZASC3:0x1BE40000-----
1bf3004c-1bf3004f : /mcinfo@1BC300000
-----TZASC4:0x1BF40000-----

After gathering all the necessary information, we can finally try to see if the exploit works. We first try to read the contents of TZASC, you can follow the following steps:

  • (1) Find an Android application that can map the camera_heap buffer and initialize it to a fixed, known value.
  • (2) At the same time, exploit the HDCP TA vulnerability and transfer the physical memory to a physical memory copy for 0x1000 bytes (TZASC register size) between 0x1BC40000 (the address of the first TZASC) and 0x99000000 (the address of camera_heap).
  • (3) In Android applications, continuously monitor camera_heap. If its content changes, it proves that the TA vulnerability is exploited successfully.
  • (4) Save the buffer contents to a file for further analysis.

In fact, after a few seconds, the execution completes and the dump of TZASC is saved to a file. We then use a hex editor to analyze the contents of the dump.

TZASC range dump:
The configurations of the four TZASC controllers in the system are very similar. Here we only analyze one as an example. The green marked area in the above figure represents area 0, which is the default base area (0x0000000000000000-0x000000fffffff000) we explained in the first article. Purple indicates all security scopes configured on the system at a particular time. Each security range consists of two 8-bytes (little-endian), followed by another 16 bytes for the attribute. Finally, the areas marked in red are the start and end addresses of the security range, which is the range of the TEE OS (0x00000000bab00000- 0x00000000bffff000). This also matches the information provided by /proc/iomem:

80000000-baafffff : System RAM
80090000-8197ffff : Kernel code
82380000-831b0fff : Kernel data
c0000000-dfffffff : System RAM

It can be seen that since REE cannot access this memory, there is a gap in the TEE area.

Next, we fine-tune the exploit code to cover 4 TZASCs instead of just reading. We can use the same physical ROP payload, but swap the source and destination addresses. We target the starting address of the TEE OS security range and set it to a value greater than the ending address. As a result, the scope is invalid, will be ignored, and the entire TEE memory will be configured as non-secure.

0x04 Access TEE memory

At this point, we have a running system in which all TEE memory is non-secure and can be accessed by REE. This leaves us with the final hurdle - how to access this memory?

As mentioned before, the HDCP TA cannot access it because the phys driver denies access to secure memory (even if the scope is now reconfigured as secure). Therefore, it must be accessed from the Android application. However, under normal circumstances the Linux kernel will allow this range to be mapped since it is reserved for TEEs. Therefore, during our exploit, the TA still has access to the entire non-secure memory. In this way, we found a way to solve the problem.

There may be several different ideas, and finally we decided to use Samsung's management program RKP to implement it. For an introduction to ARMv8-A privilege levels, please refer to the first article. Samsung officially provided the following picture to illustrate the security functions it implements.

RKP read-only page protection:
When there is a hypervisor in an ARMv8-A system, there is another stage of translation tables whose original intention is to allow multiple guest operating systems in the system. Therefore, the attack process is divided into two stages.

  • The first stage is when the Linux kernel executes a translation from a virtual address to a so-called intermediate physical address (IPA).
  • The second stage is when the hypervisor converts the IPA into the final physical address (PA).

The following figure shows the two-stage memory conversion process:

The hypervisor can restrict memory permissions, which means that if a page is mapped as read-only in the second phase, the Linux kernel cannot modify its contents even if the page is mapped with write permissions. Using RKP, it is possible to force certain security-related structures in Linux kernel memory to be read-only, making them unmodifiable even if an attacker executes arbitrary code in the kernel context.

Our attack idea is then to find the hypervisor's translation table corresponding to the camera_heap ION buffer and change it to point to the TEE kernel physical address. Note that it is possible to ignore the hypervisor and gain access to TEE memory by modifying structures within the Linux kernel itself. However, the hypervisor is much less complex than the Linux kernel, so we want to target the hypervisor. We assume that the second-stage page table will map to a fixed physical address and will never be modified, so we do not need to parse the complex results to find the exact memory location that needs to be modified.

To confirm whether our hypothesis is correct, we must find and extract the hypervisor memory in the system RAM. To obtain information about the system memory layout, we performed a dump of /proc/iomem, which revealed three main RAM ranges. We use the exploit to dump the physical memory of each range and then search for strings like RKP. Search results show that the hypervisor starts at physical address 0x87000000 in the first RAM range.

The second task is to find the page table entry in the hypervisor's memory that corresponds to the camera_heap ION buffer.

In principle, we could reverse engineer the hypervisor binary to find out how the TTBR register is initialized and parsed. However, since we dumped the hypervisor's memory at runtime, we decided to examine it and see if we could find the page table directly. The prerequisite for finding the page table is that we are familiar with its structure. ARM has a document on ARMv8-A address translation, which explains the format of the translation table descriptor for block entries and table entries. The image below is the format of a block entry.

The format of the block page table entry:

We used this information to find block entries in the memory dump corresponding to the ION buffer shown in the image below.

Block entry for camera_heap buffer:
The entry marked above is set to 0x990004FD (little endian). Since we only want to modify the address, we can ignore the attributes, so the output block address becomes 0x99000000, which corresponds to the address of camera_heap.

The final step is to modify our payload so that the page table entries for camera_heap are replaced with entries from the TEE kernel. In this way, when accessing camera_heap from REE, CA actually accesses TEE memory. To do this, we increased the safe range by 2MB (the size of each block page table entry) and reserved the low byte to hold the attributes set by the camera_heap (i.e. FD 04).

In this way, a successful dump of the security scope can be achieved. In it, we found several binaries such as shared libraries, loaded TAs (including the HDCP TA used for the exploit), and the TEE kernel itself. Now, under our control, memory can be mapped into REEs using page tables, so there are no restrictions on the permissions of pages, which means that all memory (including code) is writable. All modifications are reflected in the TEE's view with the same memory.

To summarize, we review that in order to fully access the TEE memory, we exploited a combination of vulnerabilities:

  • (1) The lack of GP parameter checking in HDCP TA allows us to perform arbitrary reads/writes in TA.
  • (2) Stack-based buffer overflow can be exploited to obtain arbitrary code execution in the TA context.
  • (3) Antirollback does not apply to TA, which means that even if Samsung correctly fixes the TA vulnerability, we can still load the older version of TA with the vulnerability.
  • (4) The group-based permission allocation mechanism is too granular. In fact, HDCP TA only needs to access non-secure memory, but it can still map registers after permissions are set.
  • (5) For TAs that allow access to registers, the kernel will allow mapping of registers such as TZASC and TZPC. These registers may completely defeat the security provided by TrustZone technology, so TA should not have access to them.

0x05 Summary

At this point, we have formed a complete vulnerability exploitation chain. At this stage, we successfully mapped all TEE memory into the Android application. The memory is fully readable and writable, which means an attacker can:

(1) Modify the code of TA and TEE kernels because permission control does not apply to Android applications.
(2) Implement the bypass of KASLR, PAN and PXN in the kernel.

Once the attacker fully controls the TEE, he can further implement various attacks, such as modifying the device fingerprint or facial recognition unlocking function implemented in the TEE to bypass the screen lock.

Guess you like

Origin blog.csdn.net/weixin_45264425/article/details/132669557
Recommended