- CVE-2021-40028 OOB Access in the Encap_tlv_for_hash_zip Function
- CVE-2021-40018 OOB Access in the get_sec_image_zip Function
- CVE-2021-40021 Parameter Pointers Information Leak in the check_xxx_params Functions
- CVE-2021-40025 Heap Pointers Information Leak in the eid_malloc, eid_free, malloc_eid_buffer and free_eid_buffer Functions
OOB Access in the get_sec_image_zip
Function¶
There is an OOB access in the function get_sec_image_zip
:
int get_sec_image_zip(void *ibuf0_addr, unsigned int *ibuf0_20028, unsigned int *ibuf0_20038, void *obuf3_addr) {
// [...]
img_buf = g_img_buf;
// [...]
for (int x = ibuf0_20028[0]; x <= ibuf0_20028[1]; x++) {
for (int y = ibuf0_20028[2]; y <= ibuf0_20028[3]; y++) {
*(uint8_t *)(eid_buf.addr + ...) = *(uint8_t *)(img_buf + x * 0x1E0 + y);
}
}
// [...]
}
g_img_buf
is stored in the local variable img_buf
which is then used in many loops without ever checking that it has been allocated. If it has indeed not been allocated, the accesses in the loops will be OOB.
We triggered this bug with a proof of concept and obtained the following crash:
[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1f00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x0, fault_code: 0x92000006
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TEE_EID] tid=40 is-idle=0 is-curr=0
[HM] state=BLOCKED@MEMFAULT sched.pol=0 prio=46 queued=1
[HM] aff[0]=ff
[HM] flags=1000 smc-switch=0 ca=7253 prefer-ca=7253
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <get_sec_image_zip+0x200/0x360>
[HM] <?>+0x0/0x0
[HM] <proc_sec_zip_cmd>+0x34/0x48
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM]
[HM] [TRACE][1212]pid=48 exit_status=130
OOB Access in the Encap_tlv_for_hash_zip
Function¶
This is another OOB access in the Encap_tlv_for_hash_zip
function:
int Encap_tlv_for_hash_zip(int hash, int hash_len, int zip, int zip_len, uint8_t *hash_tlv) {
// [...]
*(uint32_t *)(hash_tlv + 0) = 2;
SLog("%s: Tlv hash type = %u\n", "[Trace]", 2);
*(uint32_t *)(hash_tlv + 4) = hash_len;
SLog("%s: Tlv hash_len = %u\n", "[Trace]", hash_len);
if (memcpy_s(hash_tlv + 8, 0x1FFF8, hash, hash_len)) {
SLog("%s: memcpy_s err size is %d, len is %d\n", "[Error]", 0x1FFF8, hash_len);
return -1;
}
printHexWithTag("Sm3 hash", hash, hash_len);
*(uint32_t *)(hash_tlv + hash_len + 8) = 1;
SLog("%s: Tlv zip type = %u\n", "[Trace]", 1);
*(uint32_t *)(hash_tlv + hash_len + 0xc) = zip_len;
SLog("%s: Tlv zip_len = %u\n", "[Trace]", zip_len);
if (memcpy_s(hash_tlv + hash_len + 0x10, 0x1FFF0 - hash_len, zip, zip_len)) {
SLog("%s: memcpy_s err size is %d, len is %d\n", "[Error]", 0x1FFF0 - hash_len, zip_len);
return -1;
}
return hash_len + 0x10 + zip_len;
}
The value of hash_len
is capped to 0x1FFF8 because of the first memcpy_s
. But for hash_len
values between 0x1FFF1 and 0x1FFF8, the following behavior can be observed:
- the write of the TLV type (1) at offset
hash_len + 8
can be OOB - the write of the TLV length (
zip_len
) at offsethash_len + 0xC
can be OOB - the destination of the
memcpy_s
at offsethash_len + 0x10
can be OOB - the size of the
memcpy_s
(0x1FFF0 - hash_len
) can underflow
The memcpy_s
function will detect a negative size, so this behavior cannot be exploited. The OOB write of the TLV type and length will happen out-of-bounds, but they don't crash the trustlet/allocator. We suspect this is because the allocation is mmap'ed, and the size (including the metadata) is page aligned, resulting in padding after the allocation.
Nevertheless, by triggering this bug with a proof of concept, we can see the negative size (-4), proving the TLV type and length OOB write accesses:
[TEE_EID-1] [Trace]: ------ TA_InvokeCommandEntryPoint ------
[TEE_EID-1] [Trace]: Recived the commond, id is 9
[TEE_EID-1] [Trace]: Recived the unsec zip message
[TEE_EID-1] [Trace]: Into the proc_unsec_zip_cmd function
[TEE_EID-1] [Trace]: Malloc_eid_buffer, addr = 0x375f010, len = 131072
[TEE_EID-1] [Trace]: Tlv hash type = 2
[TEE_EID-1] [Trace]: Tlv hash_len = 131060
[TEE_EID-1] [Trace]: Tlv zip type = 1
[TEE_EID-1] [Trace]: Tlv zip_len = 4096
[TEE_EID-1] [Error]: memcpy_s err size is -4, len is 4096
[TEE_EID-1] [Error]: Encap_tlv_for_hash_zip failed!
[TEE_EID-1] [Trace]: Free_eid_buffer, addr = 0x375f010
Parameter Pointers Information Leak in the check_xxx_params
Functions¶
There are information leaks in the functions checking the input parameters for each of the commands, including the check_common_params
function (at 0x71A8):
unsigned int check_common_params(int paramTypes, TEE_Param *params) {
// [...]
if (params[0].memref.size != 4 || !params[0].memref.buffer) {
SLog("%s: Invalid param[0], size is %u, buffer address is %p\n\n", "[Error]",
params[0].memref.size, params[0].memref.buffer);
return 0xFFFF0006;
}
// [...]
}
All of these functions print the parameter buffer address when its size is incorrect. This will reveal that the buffer is always mapped at the same address. Below is a log message sample:
[TEE_EID-1] [Trace]: Recived the commond, id is 6
[TEE_EID-1] [Trace]: Recived the id info message
[TEE_EID-1] [Error]: Invalid param[3], size is 2056, buffer address is 0x70004000
Heap Pointers Information Leak in the eid_malloc
, eid_free
, malloc_eid_buffer
and free_eid_buffer
Functions¶
There are information leaks in the allocation and deallocation functions, including eid_malloc
, eid_free
, malloc_eid_buffer
, and free_eid_buffer
:
void *eid_malloc(int len, int hint) {
// [...]
addr = TEE_Malloc(len, hint);
SLog("%s: Eid_malloc, addr = %p, len = %u\n", "[Trace]", addr, len);
return addr;
}
void eid_free(void *addr) {
SLog("%s: Eid_free, addr = %p\n", "[Trace]", addr);
TEE_Free(addr);
}
int malloc_eid_buffer(eid_buffer_t *buf, int size) {
// [...]
buf->addr = TEE_Malloc(size, 0);
// [...]
buf->size = size;
SLog("%s: Malloc_eid_buffer, addr = %p, len = %u\n", "[Trace]", buf->addr, size);
return 0;
}
int free_eid_buffer(eid_buffer_t *buf) {
// [...]
buf->size = 0;
SLog("%s: Free_eid_buffer, addr = %p\n", "[Trace]", buf->addr);
return TEE_Free(buf->addr);
}
These functions print the address of the allocated/freed buffer. This can be useful information when developing a heap exploit. Below is a log message sample:
[TEE_EID-1] [Trace]: Malloc_eid_buffer, addr = 0x375f010, len = 131072
...
[TEE_EID-1] [Trace]: Free_eid_buffer, addr = 0x375f010
Affected Devices¶
We have verified that the vulnerabilities impacted the following device(s):
- Kirin 990: P40 Pro (ELS)
Please note that other models might have been affected.
Patch¶
Name | Severity | CVE | Patch |
---|---|---|---|
OOB Access in the Encap_tlv_for_hash_zip Function |
Critical | CVE-2021-40028 | January 2022 |
OOB Access in the get_sec_image_zip Function |
High | CVE-2021-40018 | January 2022 |
Parameter Pointers Information Leak in the check_xxx_params Functions |
Medium | CVE-2021-40021 | January 2022 |
Heap Pointers Information Leak in the eid_malloc , eid_free , malloc_eid_buffer and free_eid_buffer Functions |
Medium | CVE-2021-40025 | January 2022 |
Timeline¶
- Nov. 05, 2021 - A vulnerability report is sent to Huawei PSIRT.
- Nov. 16, 2021 - Huawei PSIRT acknowledges the vulnerability report.
- Jan. 01, 2022 - Huawei PSIRT states that these issues were fixed in the January 2022 update.
- From Nov. 30, 2022 to Jul, 19 2023 - We exchange regularly about the release of our advisories.
Copyright © Impalabs 2021-2023