We have identified a new stack buffer overflow vulnerability in Unisoc’s TrustZone implementation. The vulnerability can be exploited to achieve arbitrary code execution in the DRM Trustlet’s runtime.
The vulnerability we are disclosing in this advisory affected a wide range of Unisoc devices, including phones on the newest chipsets. The August 2023 issue of the Unisoc Security Bulletin contains this vulnerability as CVE-2023-33913.
Vulnerability Details
The Trusted Execution Environment (TEE) implementation of Unisoc Tiger chipsets on certain devices uses a modified version of Google’s TEE implementation called Trusty.
Trusty is an open-source trusted OS based on Little Kernel. The kernel is running in 64bit mode, however, the trustlets are 32bit ELF images baked into the TOS binary together with the kernel image. These trustlets include typical functionalities for a TEE: gatekeeper, keystore, cryptographic functions.
The trustlet implementing the later (trusty-oemcrypto) is responsible for performing cryptographic functions, mainly used for DRM solutions. One part of the API implements the standard Widevine, while the other part is Unisoc’s own solution, the Unisoc OEMCrypto API. The stack-based buffer overflow was identifed in the implementation of this API.
The API can be accessed on the “com.android.trusty.oemcrypto” port and exposes many functionalities, like generating a random number, managing keys, and generating signatures.
To access these functionalities, the userspace program needs to open the device file located at /dev/trusty-ipc-dev0.
To open this device, the attacker needs to be in the system group and have the teetz_device selinux permission.
The TIPC_IOC_CONNECT ioctl command of this driver is used to connect to a port running in the TEE, the name of the port as a string is provided in the data parameter of the ioctl call, for example:
int main() {
int fd, ret;
fd = open("/dev/trusty-ipc-dev0", O_RDWR);
if(fd < 0){
// ...error
}
ret = ioctl(fd, TIPC_IOC_CONNECT, "com.android.trusty.oemcrypto");
//...
After this, the read and write system calls on the opened file descriptor are used to send and receive data from the Non-Secure World to the Secure World.
The trustlet-specific structures for sending/receiving data are not standard or documented. All structures start with an unsigned integer which contains the command number of the function. Here is an example, which is the structure needed to get a randomly generated number from the oemcrypto trustlet:
struct oemcrypto_msg_getrandom {
unsigned int op;
unsigned int len;
};
Before using the GetRandom or any other functions, OEMCrypto for this channel needs to be initialized.
This can be done with the oemcrpyot_msg_init structure, which has to contain the function number and an additional unsigned integer value (which is not relevant for the purposes of this vulnerability).
For example:
// init
struct oemcrypto_msg_init msg1;
msg1.op = OEMCRYPTO_CMD_INIT;
msg1.value = 0xdeadbeef;
ret = write(fd, &msg1, sizeof(msg1));
// getrandom
struct oemcrypto_msg_getrandom msg2;
msg2.op = OEMCRYPTO_CMD_GET_RANDOM;
msg2.len = 0x10;
ret = write(fd, &msg2, sizeof(msg2));
ret = read(fd, buff, 12 + 0x10); // random value after the first 12 bytes
// terminate
struct oemcrypto_msg_terminate msg3;
msg3.op = OMECRYPTO_CMD_TERMINATE;
ret = write(fd, &msg3, sizeof(msg3));
The vulnerability can be found in the UNISOC_OEMCrypto_DecryptCENC function.
The function begins with a validation that the input data is not null, then the following happens:
uint UNISOC_OEMCRYPTO_DecryptCENC(decrypt_cenc_msg *msg, uint len, ..., ...){
uint data0;
uint size1;
char data3[8];
char data2[16];
char data1[24];
// ...
OPENSSL_memcpy(data0, msg, 4);
OPENSSL_memcpy(size1, &msg->msg_size, 4);
OPENSSL_memcpy(data1, &msg->msg, size1);
// ...
}
As we can see above, in the provided message there is a size field and a data field.
The size value is read first, then the value is used to copy the specified number of bytes from the message onto a stack buffer.
Since the size field is controlled by the sender, this is a textbook stack based buffer overflow.
The rest of the function does not really matter, since no checks are placed before this memcpy besides the null validation, and no stack smashing protection hardening is used.
The resulting crash dump can be observed in dmesg.
Affected Devices
All Unisoc chipsets containing the TrustZone implementation and the DRM Trustlet, namely: T606/T612/T616/T610/T618/T760/T770/T820/S8000
Fix
Unisoc OTA images, released after August 2023, contain the fix for the vulnerability.
Timeline
- 2022.05.16. Bug reported to Unisoc PSIRT
- 2022.06.16. TASZK asks for update
- 2022.06.19. Unisoc confirms vulnerability
- 2022.06.26. Unisoc confirms CVE and bulletin release date
- 2022.08.04. Unisoc releases security bulletin on confirmed date
- 2025.10.01. Advisory release