Android Trusted Execution Environment Security Research (2): Trusted Application Vulnerability Exploitation

0x00 Preface

In this second series of articles, we will continue to explore TEEGRIS through reverse engineering techniques to discover and exploit existing security vulnerabilities.

0x01 TA vulnerability analysis

1.1 Global Platform (GP) interface

As mentioned in the first article, TA implements the GP interface . The entry point we are most concerned about is TA_InvokeCommandEntryPoint, which is executed every time the client application (CA) sends a command to TA.
In the GP specification, the prototype of this function is documented.
Description of TA_InvokeCommandEntryPoint:
The API allows the CA to specify up to 4 parameters . Each parameter has an associated parameter type.

Optional parameter types are also listed in this GP document.

GP parameter type:

Assuming that a parameter is used here (so its type is not TEE_PARAM_TYPE_NONE), there are two main types involved here, namely value and memref. The value of each parameter type is encoded as a nibble and combined together to form the paramTypes parameter.

TEE_Param is a union type, which is defined as follows.

TEE_Param Union definition:

Depending on whether the parameter is a value type or a memref type, its filling content is also different:

  • (1) value: Members a and b are set to exactly the same value as the value passed by CA.
  • (2) memref: CA passes a reference to the buffer in its private memory.

**TEE OS will map the same memory in TA's address space and fill the union's buffer and size members accordingly. **Specifically, the buffer will point to a guaranteed valid virtual address in the TA memory space. This way, both TA and CA will have a view of the same physical memory.

As is obvious from the description, it is crucial to check the parameter types. If TA expects a memref but CA passes a value instead , there is no guarantee that buffer or a points to a valid shared memory location. This can lead to type confusion, where the CA has full control over what buffer pointers and sizes the TA considers valid.

1.2 GP examination in TA

Considering the importance of this check, it is indeed necessary for us to verify that all TAs implement this check correctly. Surprisingly, it seems that not all TAs have perfect checks.
TIMA TA parameter type verification:

TA first checks the paramTypes parameters correctly and returns an error if they are not the expected values. This is expected behavior and presents no risk.
Now, we switch to another TA, which is the HDCP TA (UUID 00000000-0000-0000-0000-000048444350).

HDCP TA parameter type verification:

This TA does not contain any checking of parameter types. These parameters can be taken from the input and passed directly to the main function. This means that type confusion attacks can be carried out on this TA.

Assuming we can now set the input parameter to any value we want, we can try to find a way to utilize that parameter. Ideally, we would like to use it to achieve arbitrary reading and writing, and then use it to gain runtime control of the TA.

After analyzing all the commands implemented in TA, we found that three of them can be combined to obtain read and write primitives. Since the GP API doesn't define any standard commands, we don't know the exact purpose of implementing the commands in TA. They are TA-specific, and the same command value in different TAs may have completely different functions. Our target commands are FB, FC and CB.

(1) FB command:

This command takes the input buffer from the Android application , wraps it into a secure object (meaning the buffer has been encrypted and signed using the TA private key), and returns the secure object to the application.

Due to a type confusion vulnerability, an attacker has full control over input and output pointers. However, since the output is unpredictable, the command itself does not provide very powerful primitives.

(2) FC command:

The FC command does the opposite of the FB command: it takes a safe object and unpacks it back to the original content**. However, the unpacked output is not returned to the REE but is stored in a fixed memory location within the TA. **

(3)CB command:


Finally, the CB command takes the buffer containing the unpacked data and returns a portion of it to the REE. Note that the target address can also be set to point to TA internal memory due to a type confusion vulnerability.

Usually, the scenarios where the three commands are used in combination are as follows:

The FB command receives input from REE, encrypts it, and returns it to REE .

The REE uses the FC command to pass it to the TA, which decrypts it and stores it in the TA internal memory location.

Finally, use the CB command to return a portion of the buffer to the REE. The end result is that the output of the CB command is a partial copy of the input to the FB command.

Arbitrary reading can be achieved by specifying the input of the FB command to point to the TA internal memory, and specifying the output of the CB command to point to the REE memory.

Perform a type confusion attack on FB command input to extract TA memory:


Similarly, arbitrary writing can also be achieved by setting the input of the FB command to REE memory and setting the output of the CB command to TA memory.
Type confusion attack on CB command output to overwrite TA memory:

Now, we can achieve arbitrary reading and writing in HDCP TA. Since TA is considered safe and completely isolated from the untrusted Android OS, this is already a big breakthrough for us. However, Samsung has implemented exploit mitigations, so there are two remaining questions here:

  • (1) Due to ASLR, we don't know the exact memory mapped location of TA.
  • (2) Because we want to use TA to escalate privileges and gain complete access to the entire TEE memory , we may also want to gain code execution .

1.3 Bypassing ASLR

As mentioned in the previous article, the principle of ASLR is to randomize code and data segments. The random range is a random number between 0 and 32767 multiplied by 4096 (page size).

We didn't find a way to somehow leak information (such as a pointer value) that would allow us to recover the random offset.

What happens if we just combine the above three commands and try to read from a random location? Actually, TA will crash and in Android we will be able to see the error log in dmesg.

[ 119.608950] SW> [TEEgris:SCrypto] SCrypto 2.4 is in FIPS approved mode
[ 119.608979] SW> HDCP : TA_CreateEntryPoint
[ 119.608992] SW> HDCP : TA_OpenSessionEntryPoint
[ 119.612625] SW> SECUREOS VERSION: Samsung Secure OS Release Version 3.1.0.0 (15415496 15403694) built on: 2019-02-14 16:37:50, binary version: 9e91f07b
[ 119.612651] SW> ERR: UNKNOWN TEE_MemMove() pid=114: Panic Reason: check of [inbuf] parameter is failed in ID_TEE_MemMove
[ 119.612665] SW> SECUREOS VERSION: Samsung Secure OS Release Version 3.1.0.0 (15415496 15403694) built on: 2019-02-14 16:37:50, binary version: 9e91f07b
[ 119.612678] SW> ERR: UNKNOWN _TEE_Panic() pid=114: Function No: 0x607, Specification No: 0xa
[ 119.612688] SW> SECUREOS VERSION: Samsung Secure OS Release Version 3.1.0.0 (15415496 15403694) built on: 2019-02-14 16:37:50, binary version: 9e91f07b
[ 119.612699] SW> ERR: UNKNOWN _TEE_Panic() pid=114: Panic thrown out from file:
[ 119.612706] SW> src/teesl/tee_string.c,
[ 119.612712] SW> Line - 20,
[ 119.612719] SW> Code - 0x607
[ 119.612727] SW> Samsung Secure OS Release Version 3.1.0.0 (15415496 15403694) built on: 2019-02-14 16:37:50, binary version: 9e91f07b
[ 119.612734] SW> Th#222 of Pid=114 panicked with code: 0x00000607

However, it does not prevent us from trying to interact with the same TA again, nor does it prevent access to the same random address. This time, let's see if we're lucky enough to hit real mapped memory.

This process can be repeated multiple times until the address we are trying to access is mapped. Since there are only 32k possible random numbers, finding a true random number is not that difficult and can usually be done in less than a minute. Although this efficiency is not ideal during vulnerability exploitation, it is still acceptable.

In addition, the chance that we actually encounter a valid random number is greater than 1/320000, because we already have the arbitrary read primitive and only need to find and read a page from the mapped binary file. After extracting the contents of memory, we can match them against known binary contents to retrieve the actual random offset.

1.4 Implement code execution

At this stage, we already have arbitrary read and write permissions and an ASLR bypass. After having such powerful primitives, leveraging them to obtain code execution is not a difficult problem. There could have been a few different ideas, and ultimately we decided to combine it with another stack buffer overflow vulnerability found in TA.

The command TZ_RepeaterAuth_Send_ReceiverId_List_T() receives some information in the request buffer, performs some validation on the passed parameters, and then calls the function TZ_RepeaterAuth_Send_ReceiverId_List20_T() or TZ_RepeaterAuth_Send_ReceiverId_List21_T() depending on the set HDCP version. However, as shown in the figure below, the parameter verification process only compares two values ​​provided by the attacker, namely request[3] and req_size. This way, the attacker can construct the request and ensure that the check passes.
Decompiled TZ_RepeaterAuth_Send_ReceiverId_List_T function:

Jumping to the function TZ_RepeaterAuth_Send_ReceiverId_List20_T() in the figure below, we can see that the contents of the request buffer are copied into a 160-byte array, which resides on the stack and has a size control of 5 * request[3]. This can cause a stack buffer overflow in the in buffer.
Decompiled TZ_RepeaterAuth_Send_ReceiverId_List20_T function:
This is a very classic stack buffer overflow where we control the size of the copy and the contents of the buffer. Up to 1275 bytes can be copied so there is enough space to store the shellcode. However, TA also uses Stack Canaries protection, as shown in the figure below, so exploiting the vulnerability becomes difficult.

The end part of decompiled TZ_RepeaterAuth_Send_ReceiverId_List20_T:

Since we now have arbitrary read and ASLR bypass, we can easily read the value of __stack_chk_guard and populate it into our shellcode in order to make the canary verification pass.
We try to combine all the steps to prove that it is finally possible to control the program counter (PC) in HDCP TA. When the stack canary is set to the wrong value, we can see the following debugging information in dmesg:

[38142.232347] SW> [TEEgris:SCrypto] SCrypto 2.4 is in FIPS approved mode
[38142.232374] SW> HDCP : TA_CreateEntryPoint
[38142.232381] SW> HDCP : TA_OpenSessionEntryPoint
[38142.240917] SW> TZ_SET_HDCP_VERSION_T : HDCP 2.0 version is setuped
[38142.243372] SW> *** tzsl detected *** Stack smashing
[38142.243387] SW> rettadr: 0x286f9c

After reading and setting up the canary correctly, we replace the return address on the stack with 0xAAAAAAAAA, and the following will be printed:

[37276.997902] SW> [TEEgris:SCrypto] SCrypto 2.4 is in FIPS approved mode
[37276.997921] SW> HDCP : TA_CreateEntryPoint
[37276.997928] SW> HDCP : TA_OpenSessionEntryPoint
[37277.005590] SW> TZ_SET_HDCP_VERSION_T : HDCP 2.0 version is setuped
[37277.007939] SW> Samsung Secure OS Release Version 3.1.0.0 (15415496 15403694) built on: 2019-02-14 16:37:50, binary version: 9e91f07b
[37277.007952] SW> Th#2528 of Pid=5344 panicked with signal: 5 (SIGTRAP)
[37277.007960] SW> Fault addr 0xaaaaaaaa in module N/A

At this point, the first part of TA vulnerability exploitation comes to an end. We get arbitrary reads, writes, and code execution in the HDCP TA. However, thanks to XN, we can only reuse existing code, which will be analyzed more in the next article.

0x02 TA anti-rollback mechanism

During our research, we discovered that Samsung’s latest security patch announcement at the time included the following.
Samsung version release announcement, involving HDCP TA parameter type checking:


It seems that a researcher reported the missing parameter check to Samsung and it was fixed. But is the fix really the right one? Let's take a look at the new TA_InvokeCommandEntryPoint.
TA_InvokeCommandEntryPoint in new HDCP TA:

The function seems to implement appropriate checks on parameter types. Stack-based buffer overflow vulnerabilities still exist, but without a leaky canary we can't exploit them. In this way, our previous achievements will be in vain.

But this is not the case and we find that the TA is just a code signature blob passed by the REE giving the executed TEE. What happens if the REE asks the TEE to execute an older version of TA? In the previous article, we saw that the anti-rollback function can be enabled depending on the TA version. SEC2 version does not support this function, but SEC3 and SEC4 support it. What is the version specified in the new HDCP TA header? We open it with a hex editor.

New TA header:

It seems that the new TA version is still SEC2! This means that there will not be any anti-rollback mechanism. We can then verify by upgrading the phone's firmware to a newer version and creating a modified copy of libteecl.so that will look for the TA in /data/local/tmp instead of /vendor/tee. In this way, we can place the old TA in /data/local/tmp and let the TEE execute the old version of the TA.

In fact, TEEGRIS OS allows loading of older versions of TA that still contain exploitable vulnerabilities. This means that even if the new version of TA correctly fixes the parameter checking problem, an attacker can force the TEE to load the old version of TA to return to a vulnerable state.

0x03 Summary

In this article, we demonstrate the detailed process of discovering and exploiting two vulnerabilities in HDCP TA, and also explain the importance of the anti-rollback mechanism in TEE.
Welcome everyone to continue to pay attention to the next article, we will analyze in detail how to escalate privileges and gain complete access to the entire TEE memory.

Guess you like

Origin blog.csdn.net/weixin_45264425/article/details/132668376