二进制漏洞分析-30.华为TrustZone Huawei_TSS_TA漏洞
2024-1-1 15:47:18 Author: 安全狗的自我修养(查看原文) 阅读量:2 收藏

  •  

  • -5.(SMC MNTN OOB 访)

  • -10.TrustZone TEE_SERVICE_VOICE_REC

  • -19.TrustZone TCIS

  • -21.TrustZone TALoader

  • -22.TrustZone TA_uDFingerPrint

  • -23.TrustZone TA_SensorInfo

  • -24.TrustZone TA_HuaweiWallet

  • -25.TrustZone TA_SignTool OOB Read

  • -26.TrustZone IfaaKey_TA

  • -27.TrustZone Ifaa

  • 二进制漏洞分析-29.Huawei TrustZone HuaweiNfcActiveCard 漏洞

华为TrustZone Huawei_TSS_TA漏洞

此通报包含有关以下漏洞的信息:

  • HWPSIRT-2021-49134 TA_GetPayload中的堆栈缓冲区溢出

  • HWPSIRT-2021-68415 TA_DecryptSKWithCBC 和 TA_DecryptSKWithGCM 中的堆缓冲区溢出和堆栈缓冲区溢出

  • HWPSIRT-2021-53459 TA_Gen_Sysintegrity_Jws中的堆缓冲区溢出

  • HWPSIRT-2021-45148 TA_GetPayload中的参数缓冲区过度读取

  • HWPSIRT-2021-18937 TA_GetSysintegritySignStr中的参数缓冲区过度读取

  • HWPSIRT-2021-61962 TA_DecryptKEK中的参数缓冲区过度读取

  • HWPSIRT-2021-22378 hkdf_expand中的参数缓冲区过度读取

  • HWPSIRT-2021-18804 CMD_TSS_GET_PKI_CERT 和 CmdVerifySignature 中的有限越界访问

参数缓冲区过度读取TA_GetPayload

命令 (ID #0x40018) 调用了一个缓冲区过度读取。TA_GetPayloadCMD_TSS_GET_ROOT_STATUS

TEE_Result TA_InvokeCommandEntryPoint(
void *sessionContext,
uint32_t commandID,
uint32_t paramTypes,
TEE_Param params[4])
{
// [...]
switch (commandID) {
// [...]
case 0x40018:
uint32_t payload_p = 0;
uint32_t payload_len = 0;
// Retrieves the header from the TEE_Param input buffer
TA_GetPayload(params[1].memref.buffer, &payload_p, &payload_len);
// [...]
break;
}
// [...]
}

该函数从第二个输入缓冲区的偏移处读取长度。然后,使用此用户控制的值来分配从输入缓冲区复制字节的缓冲区。如果大于 的大小,则会导致任意缓冲区过度读取。result_lenTEE_Paraminbuf0xA22resultresult_lenresult_leninbuf

int TA_GetPayload(uint8_t *inbuf, char **payload_p, uint32_t *result_p) {
// ...
result_len = *(uint32_t *)(inbuf + 0xA22);
result = tss_mallocObject(result_len);
if (result_len > 2)
memcpy_s(result, result_len - 2, inbuf + 0x50F, result_len - 2);
// ...
}

概念验证代码演示了触发输入缓冲区过度读取会导致地址处的读取内存访问错误:0x70007000

[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 2300000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x70007000, fault_code: 0x92000007
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[Huawei_TSS_TA] 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=7216 prefer-ca=7216
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] sp > fpDump task states END
[HM]
[HM] [TRACE][1212]pid=44 exit_status=130

堆栈缓冲区溢出TA_GetPayload

命令 (ID #0x40018) 调用的函数中存在缓冲区溢出。TA_GetPayloadCMD_TSS_GET_ROOT_STATUS

TEE_Result TA_InvokeCommandEntryPoint(
void *sessionContext,
uint32_t commandID,
uint32_t paramTypes,
TEE_Param params[4])
{
// [...]
switch (commandID) {
// [...]
case 0x40018:
uint32_t payload_p = 0;
uint32_t payload_len = 0;
// Retrieves the header from the TEE_Param input buffer
TA_GetPayload(params[1].memref.buffer, &payload_p, &payload_len);
// [...]
break;
}
// [...]
}

此函数使用从输入缓冲区分析的信息生成 JSON 对象。用户提供的值之一是 。 然后使用此值填充大小为 10 的数组,其中包含指向嵌入在输入缓冲区内的摘要的指针:TEE_Paramdigests_countTA_GetPayloaddigests_arrayTEE_Paraminbuf

int TA_GetPayload(uint8_t *inbuf, char **payload_p, uint32_t *result_p) {
// ...
uint8_t *digests_array[10];
// ...
digests_count = *(uint32_t *)(inbuf + 0xC);
for (i = 0; i < digests_count; ++i)
digests_array[i] = &inbuf[0x32 * i + 0x31A];
apkCertificateDigestSha256_str = json_format_array("apkCertificateDigestSha256",
digests_array, digests_count);
// ...
}

但是,由于我们可以控制 ,因此可以指定一些摘要,以便我们开始在堆栈上溢出值,而不是 ,并将它们替换为指向用户控制数据的指针。digests_countdigests_array

digests_array[i] = input + 0x31A + 0x32 * i;

通过触发堆栈缓冲区溢出到足以覆盖保存的寄存器,我们最终会得到指向输入缓冲区内内存的指针,这些指针在退出时被放入这些寄存器中。{R4-R11,LR}TEE_ParamTA_GetPayload

第一个概念证明表明,通过覆盖寄存器,我们可以在输入缓冲区内的地址上获得中止指令:LRTEE_Param

[HM] ESR_EL1: 8200000f, ELR_EL1: 70004928, FAR is not valid
[HM] Huawei_TSS_TA vm fault prefetch abort: 70004928
[HM] fault: 8200000f tcb cref 1e00000028
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <?>+0x0/0x0
[HM] [ERROR][2496]process 1e00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x70004928, fault_code: 0x8200000f
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[Huawei_TSS_TA] 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=7305 prefer-ca=7305
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <?>+0x0/0x0
[HM] Dump task states END
[HM]
[HM] [TRACE][1212]pid=44 exit_status=130

第二个概念验证将覆盖已保存的寄存器,直到 帧指针。退出时,帧指针将指向用户控制的内存,因此从帧加载的任何值现在都是用户控制的。在其中,我们指向0x41414141,当以下指针访问此指针时会导致崩溃:R11TA_GetPayloadpayloadstrlen

[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1e00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x41414141, fault_code: 0x92000006
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[Huawei_TSS_TA] 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=7588 prefer-ca=7588
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <strlen+0x8/0x74>
[HM] <TA_InvokeCommandEntryPoint>+0x1900/0x1e1c
[HM] Dump task states END
[HM]
[HM] [TRACE][1212]pid=46 exit_status=130

第三个概念证明覆盖了帧指针和 ,但使其指向用户控制的内存而不是0x41414141。我们使此内存看起来像一个合法的堆分配缓冲区,以便可以在此指针上调用它。我们使用经典的“堆取消链接”技术来触发对地址 0x41414141+8 的任意写入:payloadTEE_Free

[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1e00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x41414149, fault_code: 0x92000046
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[Huawei_TSS_TA] 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=7620 prefer-ca=7620
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <?>+0x0/0x0
[HM] invalid fp. backtrace abort
[HM] Dump task states END
[HM]
[HM] [TRACE][1212]pid=50 exit_status=130

参数缓冲区过度读取TA_GetSysintegritySignStr

命令 (ID #0x40020) 调用了一个缓冲区过度读取。TA_GetSysintegritySignStrCMD_TSS_GET_SYSINTEGRITY_JWS

TEE_Result TA_InvokeCommandEntryPoint(
void *sessionContext,
uint32_t commandID,
uint32_t paramTypes,
TEE_Param params[4])
{
// [...]
switch (commandID) {
// [...]
case 0x40020:
sig_str = 0;
sig_str_len = 0;
decrpypted_sk_len = 0;
TA_GetSysintegritySignStr(
params[1].memref.buffer, &sig_str, &sig_str_len);
// [...]
break;
}
// [...]
}

该函数从第二个输入缓冲区的偏移处读取长度。然后,使用此用户控制的值来分配从输入缓冲区复制字节的缓冲区。如果大于 的大小,则会导致任意缓冲区过度读取。result_lenTEE_Paraminbuf0xB00resultresult_lenresult_leninbuf

uint32_t TA_GetSysintegritySignStr(
uint8_t *inbuf, uint8_t **sig_str_p, uint32_t *sig_str_len_p)
{
// [...]
result_len = *(uint32_t *)(inbuf + 0xB00);
result = tss_mallocObject(result_len);
if (result_len > 2)
memcpy_s(result, result_len - 2, inbuf + 0x5ED, result_len - 2);
// [...]
}

概念验证表明,触发输入缓冲区过度读取会导致地址处的读取内存访问错误:0x70007000

[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 2300000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x70007000, fault_code: 0x92000007
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[Huawei_TSS_TA] 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=7593 prefer-ca=7593
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] sp > fpDump task states END
[HM]
[HM] [TRACE][1212]pid=51 exit_status=130

参数缓冲区过度读取TA_DecryptKEK

命令 (ID #0x40020) 调用了一个缓冲区过度读取。TA_DecryptKEKCMD_TSS_GET_SYSINTEGRITY_JWS

TEE_Result TA_InvokeCommandEntryPoint(
void *sessionContext,
uint32_t commandID,
uint32_t paramTypes,
TEE_Param params[4])
{
// [...]
switch (commandID) {
// [...]
case 0x40020:
// Call to TA_GetSysintegritySignStr
decrypted_sk_len = 0;
decrypted_sk = tss_mallocObject(0x400);
TA_DecryptSK(
params[0].memref.buffer, decrypted_sk, &decrypted_sk_len);
// [...]
break;
}
// [...]
}

此命令先将第一个输入缓冲区传递给函数,然后再将其传递给 。TEE_ParamTA_DecryptSKTA_DecryptKEK

TEE_Result TA_DecryptSK(uint8_t *inbuf, uint8_t *key, int *key_len_p) {
// [...]
kek_buf_len = 0;
kek_buf = tss_mallocObject(0x400);
TA_DecryptKEK(inbuf, kek_buf, &kek_buf_len);
// [...]
}

TA_DecryptKEK然后使用函数 从偏移量为 64 处的输入缓冲区解码 base0 编码的字符串。b64_decode_ex

TEE_Result TA_DecryptKEK(
uint8_t *inbuf, uint8_t *outbuf, uint32_t *outbuf_len_p)
{
// [...]
uint32_t b64_encoded_len = *(uint32_t *)(inbuf + 0x400);
uint32_t b64_decoded_len = 0;
b64_decode_ex(inbuf, b64_encoded_len, &b64_decoded_len);
// [...]
}

b64_decode_ex作为参数:

  • 包含 base64 编码字符串的输入缓冲区;

  • 编码字符串的大小;

  • 指向整数的指针,该整数将包含解码字符串的大小。

在我们的例子中,编码字符串的大小由用户控制,并从偏移处的输入缓冲区读取。但是,该函数从不检查是否小于输入缓冲区的大小,如果用户提供的值足够大,则会导致缓冲区过度读取。b64_encoded_len0x400b64_encoded_len

概念验证表明,触发输入缓冲区过度读取会导致地址处的读取内存访问错误:0x70007000

[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 2300000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x70007000, fault_code: 0x92000007
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[Huawei_TSS_TA] 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=7638 prefer-ca=7638
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <b64_decode_ex+0x74/0x294>
[HM] <memset_s>+0x28/0x38
[HM] <TA_DecryptKEK>+0x58/0x148
[HM] <TA_DecryptSK>+0x6c/0x2f4
[HM] <TA_InvokeCommandEntryPoint>+0x1cb0/0x1e1c
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM]
[HM] [TRACE][1212]pid=56 exit_status=130

堆缓冲区溢出和堆栈缓冲区过度读取和TA_DecryptSKWithCBCTA_DecryptSKWithGCM

函数中存在堆缓冲区溢出和堆栈缓冲区溢出。TA_DecryptSKWithCBC

它由命令 (ID #0x40020) 调用。CMD_TSS_GET_SYSINTEGRITY_JWS

TEE_Result TA_InvokeCommandEntryPoint(
void *sessionContext,
uint32_t commandID,
uint32_t paramTypes,
TEE_Param params[4])
{
// [...]
switch (commandID) {
// [...]
case 0x40020:
// Call to TA_GetSysintegritySignStr
decrypted_sk_len = 0;
decrypted_sk = tss_mallocObject(0x400);
TA_DecryptSK(
params[0].memref.buffer, decrypted_sk, &decrypted_sk_len);
// [...]
break;
}
// [...]
}

TA_DecryptSKWithCBC由函数调用。TA_DecryptSK

TEE_Result TA_DecryptSK(uint8_t *inbuf, uint8_t *key, uint32_t *key_len_p) {
// ...
aes_cbc_args_t args;
// ...
kek_buf_len = 0;
kek_buf = tss_mallocObject(0x400);
TA_DecryptKEK(inbuf, kek_buf, &kek_buf_len);
object = TSS_ImportKey(kek_buf, kek_buf_len);
// ...
if (*(uint32_t *)(inbuf + 0xA58) == 0) {
*key_len_p = *(uint32_t *)(inbuf + 0x880);
args.dest = key;
args.dest_len_p = key_len_p;
// ...fill the args structure...
memcpy(&args, inbuf + 0x48C, 0x4C4);
TA_DecryptSKWithCBC(args);
}
// ...
}

TA_DecryptSKWithCBC用于使用 AES-CBC 解密密钥。它通过堆栈给出其参数,使用我们称为:aes_cbc_args_t

  • 源缓冲区是TEE_Param输入缓冲区(用户控制)中数据堆栈的副本

  • 源长度也是TEE_Param输入缓冲区中值的堆栈的副本(用户控制)

  • 大小为 0x400 的目标缓冲区分配在调用方的堆上decrypted_skTA_DecryptSK

  • 目标长度未指定(在调用方中设置为 0)decrypted_sk_lenTA_DecryptSK

TEE_Result TA_DecryptSKWithCBC(aes_cbc_args_t args) {
// ...
TSS_AES_DES_Enc_Dec(
args.key, args.iv, args.iv_len,
TEE_ALG_AES_CBC_PKCS5, 1,
args.src, args.src_len,
args.dest, args.dest_len_p);
// ...
}

源长度也将用作 AES-CBC 操作中的目标长度。如果我们指定一个大于 0x400 的源长度,从而指定一个目标长度,我们将溢出堆分配的缓冲区。如果我们指定更大的源长度,我们也会过度读取堆栈缓冲区。

同样,采用通向的路径也可能导致堆缓冲区溢出和堆栈缓冲区过度读取。TA_DecryptSKWithGCM

TEE_Result TA_DecryptSK(uint8_t *inbuf, uint8_t *key, uint32_t *key_len_p) {
// ...
aes_cbc_args_t args;
// ...
kek_buf_len = 0;
kek_buf = tss_mallocObject(0x400);
TA_DecryptKEK(inbuf, kek_buf, &kek_buf_len);
object = TSS_ImportKey(kek_buf, kek_buf_len);
// ...
if (*(uint32_t *)(inbuf + 0xA58) == 1) {
*key_len_p = *(uint32_t *)(inbuf + 0x880);
args.dest = key;
args.dest_len_p = key_len_p;
// ...fill the args structure...
memcpy(&args, inbuf + 0x48C, 0x5CC);
TA_DecryptSKWithGCM(args);
}
// ...
}

第一个概念验证代码可用于触发堆缓冲区溢出。它使用源长度 0x480,导致在稍后调用时检测到损坏:TEE_Free

[Huawei_TSS_TA-1] hmstss-TA [Trace] TA_DecryptSKWithCBC: start TA_DecryptSKWithCBC
[Huawei_TSS_TA-1] hmstss-TA [Trace] TSS_AES_DES_Enc_Dec: begin to TSS_AES_DES_Enc_Dec
[Huawei_TSS_TA-1] hmstss-TA [Trace] TSS_AES_DES_Enc_Dec: begin to TEE_AllocateOperation
[Huawei_TSS_TA-1] hmstss-TA [Trace] TSS_AES_DES_Enc_Dec: begin to TEE_SetOperationKey
[Huawei_TSS_TA-1] hmstss-TA [Trace] TSS_AES_DES_Enc_Dec: begin to cipher
[Huawei_TSS_TA-1] [error] 458:Cipher dofinal failed, ret=-3
[Huawei_TSS_TA-1] hmstss-TA [Error] TSS_AES_DES_Enc_Dec: TEE_CipherDofinal, fail ret=ffff0005, srclen=480,dst_len=7d000
[Huawei_TSS_TA-1] hmstss-TA [Error] TA_DecryptSKWithCBC: TA decrypt sk with kek failed: 0xFFFD0019
[Huawei_TSS_TA-1] hmstss-TA [Error] TA_DecryptSK: Decrypt sk with kek failed: ret = fffd0019
[Huawei_TSS_TA-1] hmstss-TA [Trace] TA_DecryptSK: free tmpEncryptKey
[HM] ERROR: free: double free
[Huawei_TSS_TA-1] hmstss-TA [Error] TA_InvokeCommandEntryPoint: TA_GetSysintegritySignStr decrypt sk failed
[HM] ERROR: free: corrupted footer

受控数据(解密的密钥)可能会溢出,从而允许使用经典的堆利用技术,例如导致任意写入的“堆取消链接”。

第二个概念证明代码可用于触发堆栈缓冲区过度读取。它使用源长度 0x8000,当到达堆栈末尾时会导致数据中止:

[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1e00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0xd04000, fault_code: 0x92000007
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[Huawei_TSS_TA] 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=7954 prefer-ca=7954
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] sp > fpDump task states END
[HM]
[HM] [TRACE][1212]pid=50 exit_status=130

堆缓冲区溢出TA_Gen_Sysintegrity_Jws

命令 (ID #0x40020) 调用的函数中存在堆缓冲区溢出。TA_Gen_Sysintegrity_JwsCMD_TSS_GET_SYSINTEGRITY_JWS

TEE_Result TA_InvokeCommandEntryPoint(
void *sessionContext,
uint32_t commandID,
uint32_t paramTypes,
TEE_Param params[4])
{
// [...]
switch (commandID) {
// [...]
case 0x40020:
// [...]
sig_str = 0;
sig_str_len = 0;
TA_GetSysintegritySignStr(
params[1].memref.buffer,
&sig_str, &sig_str_len);
// [...]
decrypted_sk_len = 0;
decrypted_sk = tss_mallocObject(0x400);
TA_DecryptSK(
params[0].memref.buffer,
decrypted_sk, &decrypted_sk_len);
// [...]
outbuf_len = 0;
outbuf = tss_mallocObject(0x2800);
TA_Gen_Sysintegrity_Jws(
sig_str, sig_str_len,
decrypted_sk, decrypted_sk_len,
outbuf, &outbuf_len);
// [...]
}
// [...]
}

此命令首先调用,该命令从第二个输入缓冲区中检索到的信息生成字符串。由于此字符串的某些元素由用户控制(但不是全部),因此其长度可以任意大。TA_GetSysintegritySignStrsig_strTEE_Paramsig_str_len

int TA_GetSysintegritySignStr(uint8_t *inbuf, char **sig_str_p, size_t *sig_str_len_p) {
// ...
alg_str = json_format_key_string("alg", inbuf + 0xB43);
// ...
payload_len = /* ... + */ strlen(alg_str);
payload = tss_mallocObject(payload_len);
// ...
strncat_s(payload, payload_len, alg_str, strlen(alg_str));
// ...
payload_b64enc = b64url_encode(payload, payload_len);
// ...
sig_str_len = /* ... + */ strlen(payload_b64enc);
sig_str = tss_mallocObject(sig_str_len);
// ...
strncat_s(sig_str, sig_str_len, payload_b64enc, strlen(payload_b64enc));
// ...
*sig_str_len_p = strlen(sig_str);
*sig_str_p = sig_str;
// ...
}

然后,将此字符串及其长度传递给(在参数中,分别)。 然后复制到 ,堆分配的大小为 0x2800 的缓冲区。由于缓冲区的大小是任意的,并且由于是使用 计算的,因此可以通过指定 导致大于 0x2800 字节的 来溢出。TA_Gen_Sysintegrity_Jwssigsig_lensigoutbufsig*outbuf_len_psig_lenoutbufsig_len*outbuf_len_p

int TA_Gen_Sysintegrity_Jws(
uint8_t *sig,
uint32_t sig_len,
uint8_t *sk,
uint32_t sk_len,
uint8_t *outbuf,
uint32_t *outbuf_len_p)
{
// ...
mac = tss_mallocObject(0x80);
CmdHMAC(sk, sk_len, TEE_ALG_HMAC_SHA256, sig, sig_len, mac, &mac_len);
tmpbuf = b64url_encode(mac, mac_len);
// ...
*outbuf_len_p = sig_len + 2 + strlen(tmp_buf);
strncpy_s(outbuf, *outbuf_len_p, sig, sig_len);
// ...
}

概念证明代码会触发此堆缓冲区溢出。在代码中,使用的大小约为 0xA000,导致在到达堆结束地址时崩溃。然而,可以使用较小的大小,允许使用经典的堆利用技术,例如导致任意写入的“堆取消链接”。

[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 2200000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0xf79000, fault_code: 0x92000047
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[Huawei_TSS_TA] 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=7841 prefer-ca=7841
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <strncpy_s>+0xf4/0x104
[HM] Dump task states END
[HM]
[HM] [TRACE][1212]pid=75 exit_status=130

参数缓冲区过度读取hkdf_expand

命令 (ID #0x40004) 调用的函数中存在缓冲区过度读取。hkdf_expandCMD_TSS_SYMMETRIC_CRYPTO

TEE_Result TA_InvokeCommandEntryPoint(
void *sessionContext,
uint32_t commandID,
uint32_t paramTypes,
TEE_Param params[4])
{
// [...]
switch (commandID) {
// [...]
case 0x40020:
// [...]
ibuf0_addr = params[0].memref.buffer;
outbuf_len = params[1].memref.size + 0x20;
outbuf = tss_mallocObject(outbuf_len);
CmdEncryptDecryptData(
*(uint32_t *)(ibuf0_addr + 0x00), /* alg */
*(uint32_t *)(ibuf0_addr + 0x04), /* mode */
*(uint32_t *)(ibuf0_addr + 0x08), /* okm_len */
ibuf0_addr + 0x30, /* key_path */
*(uint32_t *)(ibuf0_addr + 0x10), /* key_path_len */
ibuf0_addr + 0x62, /* hmac_key */
*(uint32_t *)(ibuf0_addr + 0x18), /* hmac_key_len */
ibuf0_addr + 0x122, /* info */
*(uint32_t *)(ibuf0_addr + 0x20), /* info_len */
ibuf0_addr + 0x1E2, /* iv */
*(uint32_t *)(ibuf0_addr + 0x28), /* iv_len */
params[1].memref.buffer, /* inbuf */
params[1].memref.size, /* inbuf_len */
outbuf, /* outbuf */
&outbuf_len); /* outbuf_len */
// [...]
break;
}
// [...]
}
int CmdEncryptDecryptData(...) {
// ...
TSS_DeriveWorkKey(rootKeyPath, hmac_key, hmac_key_len, info, info_len, okm, okm_len);
// ...
}
int TSS_DeriveWorkKey(...) {
// ...
hkdf(hmac_key, hmac_key_len, rootKey, rootKey_len, info, info_len, okm, okm_len);
// ...
}
int hkdf(...) {
// ...
hkdf_expand(prk, prk_len, info, info_len, okm, okm_len);
// ...
}

大多数长度 、 、 、 、 是从输入缓冲区中提取的,并且未选中。具体而言,如果指定的值大于输入缓冲区的大小,则函数中将发生缓冲区过度读取,其中数据将复制到堆分配的缓冲区中。okm_lenkey_path_lenhmac_key_leninfo_leniv_lenTEE_Paraminfo_lenTEE_Paramhkdf_expandinfo

int hkdf_expand(char *prk, int prk_len, char *info, int info_len, char *okm, int okm_len) {
// ...
tmpbuf_len = info_len + 1;
tmpbuf = TEE_Malloc(tmpbuf_len, 0);
tmpbuf_off = 0;
while (1) {
memcpy_s(&tmpbuf[tmpbuf_off], tmpbuf_len - tmpbuf_off, info, info_len);
memcpy_s(&tmpbuf[tmpbuf_off + info_len], tmpbuf_len - tmpbuf_off - info_len, &c, 1);
// ...compute MAC and copy it to okm...
tmpbuf_off = macLen;
TEE_Free(tmpbuf);
// ...exit if expansion is finished...
tmpbuf_len = info_len + tmpbuf_off + 1;
tmpbuf = TEE_Malloc(tmpbuf_len, 0);
memcpy_s(tmpbuf, info_len + tmpbuf_off + 1, mac, tmpbuf_off);
}
// ...
}

可以使用概念验证代码来触发此缓冲区覆盖:

[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1e00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x70007000, fault_code: 0x92000007
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[Huawei_TSS_TA] 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=7934 prefer-ca=7934
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] sp > fpDump task states END
[HM]
[HM] [TRACE][1212]pid=57 exit_status=130

限制越界访问 和CMD_TSS_GET_PKI_CERTCmdVerifySignature

命令 (ID #0x40017) 中存在有限的越界访问。CMD_TSS_GET_PKI_CERT

/*** in the .data section ***/
char *CERT_FACTOR[1] = { /* ... */ };
/****************************/

TEE_Result TA_InvokeCommandEntryPoint(
void *sessionContext,
uint32_t commandID,
uint32_t paramTypes,
TEE_Param params[4])
{
// [...]
switch ( commandID )
{
// [...]
case 0x40017:
cert_idx = params->value.a;
if (cert_idx + 1 <= 1)
{
// [...]
factor = CERT_FACTOR[cert_idx];
factor_strlen = strlen(factor);
convert_hex_to_byte(factor, outbuf, factor_strlen);
// [...]
}
// [...]
break;
}
// [...]
}

输入值用作全局变量中的索引。但是,确保绑定到给定范围的条件存在问题:。此条件允许等于 ,这将导致对 之前的第一个整数进行越界访问。TEE_Paramcert_idxCERT_FACTORcert_idxcert_idx + 1 <= 1cert_idx-1CERT_FACTOR

/*** in the .data section ***/
struct public_key {
char *mod;
char *exp;
} KEY_FACTOR[1] = { /* ... */ };
/****************************/

TEE_Result CmdVerifySignature(
uint32_t type,
uint32_t alg,
uint32_t index,
char *inbuf,
uint32_t inbuf_len,
char *signature,
uint32_t signature_len)
{
// ...
if (index + 1 <= 1) {
// ...
convert_hex_to_byte(KEY_FACTOR[index].mod, pub_mod, 0x200);
convert_hex_to_byte(KEY_FACTOR[index].exp, pub_exp, 6);
// ...
}
// ...
}

ID 为 #0x40008 的命令调用的函数中也存在类似的越界访问。CmdVerifySignature

概念验证表明,触发越界访问 in 会导致地址处的读取内存访问错误:CMD_TSS_GET_PKI_CERT0x5b32ffc

[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1e00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x5b32ffc, fault_code: 0x92000007
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[Huawei_TSS_TA] 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=8980 prefer-ca=8980
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <TA_InvokeCommandEntryPoint+0x1750/0x1e1c>
[HM] <memset_s>+0x28/0x38
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM]

受影响的设备

我们验证了这些漏洞是否影响了以下设备:

  • 麒麟990:P40 专业版 (ELS)

请注意,其他型号可能已受到影响。

补丁

名字严厉CVE漏洞补丁
堆栈缓冲区溢出TA_GetPayload不适用(*)固定
堆缓冲区溢出和堆栈缓冲区过度读取和TA_DecryptSKWithCBCTA_DecryptSKWithGCM不适用(*)固定
堆缓冲区溢出TA_Gen_Sysintegrity_Jws不适用(*)固定
参数缓冲区过度读取TA_GetPayload不适用固定
参数缓冲区过度读取TA_GetSysintegritySignStr不适用固定
参数缓冲区过度读取TA_DecryptKEK不适用固定
参数缓冲区过度读取hkdf_expand不适用固定
限制越界访问 和CMD_TSS_GET_PKI_CERTCmdVerifySignature固定

(*)华为关于APP漏洞的中、高、严重声明:

它们通过AppGallery升级来解决。通常,不会为此类漏洞分配 CVE 编号。

时间线

  • 21年2021月<>日,华为PSIRT收到漏洞报告。

  • 12年2022月<>日 - 华为PSIRT确认该漏洞报告。

  • 从 30 年 2022 月 19 日至 2023 年 <> 月 <> 日 - 我们定期交换有关公告发布的信息。


文章来源: http://mp.weixin.qq.com/s?__biz=MzkwOTE5MDY5NA==&mid=2247490935&idx=1&sn=c9d6c32426f4cfe3b5de00df329a7d94&chksm=c0608a69ff500ef5f079af29649c278f132707702969df39a9939ce3c86ed5c948c15b4eadf0&scene=0&xtrack=1#rd
如有侵权请联系:admin#unsafe.sh