二进制漏洞分析-10.华为TrustZone TEE_SERVICE_VOICE_REC漏洞
2023-11-25 07:15:29 Author: 安全狗的自我修养(查看原文) 阅读量:9 收藏

华为TrustZone TEE_SERVICE_VOICE_REC漏洞

二进制漏洞分析-5.华为安全监控漏洞(SMC MNTN OOB 访问)

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

  • CVE-2021-40036 漏洞-2021-40036 DecryptData 中的 OOB 访问

  • CVE-2021-40010 漏洞 SendTaGmmBuf 中的堆缓冲区溢出

  • CVE-2021-40027 漏洞 还原中的 OOB 访问

  • CVE-2021-40032 漏洞-2021-40032 比较中的信息泄漏

  • CVE-2021-40014 漏洞 还原中的信息泄漏

  • HWPSIRT-2021-56065 CheckModelHash 中的 Null 指针取消引用

华为TEE_SERVICE_VOICE_REC TA符合GlobalPlatform TEE Internal Core API。因此,它从位于正常环境中的客户端应用程序接收命令。这些命令由函数处理。此 trustlet 实现了 19 个命令。TA_InvokeCommandEntryPoint

信息泄露restore

函数中存在信息泄漏:restore

int RestoreTemplate(int ival0_a, int ibuf1_addr, int ibuf1_size) {
// [...]
uid_file_content = TEE_Malloc(0x80000, 0);
// [...]
for (int i = 0; i < 0xA; i++) {
// [...]
restore(
g_voiceUserDb->templates[i].voiceType,
ibuf1_addr,
ibuf1_size,
uid_file_content,
uid_path_filesize,
SaveTemplateWithNoDecrypt);
// [...]
}
// [...]
}

int restore(unsigned int voiceType,
void *ibuf1_addr,
uint32_t ibuf1_size,
void *middle_buffer,
uint32_t middle_buffer_len,
void *save_callback)
{
// [...]
if ( ... ) {
SLog(
"%s %s: parms NULL !!!%d %p %d %p %d %p\n",
"[Error]",
"restore",
voiceType,
ibuf1_addr,
ibuf1_size,
middle_buffer,
middle_buffer_len,
save_callback);
return -1;
}
// [...]
}

中的日志字符串将泄漏 3 个指针:restore

  • ibuf1_addr、输入参数TEE_Param

  • middle_buffer,堆分配的缓冲区

  • save_callback,指向函数的指针SaveTemplateWithNoDecrypt

我们通过概念验证触发了此 bug,并获得了以下输出:

[TEE_SERVICE_VOICE_REC-1] [Error] restore: parms NULL !!!9 0x70003000 524288 0x69fe010 524288 0x3c69a8c

OOB 访问restore

函数中有一个 OOB 访问:restore

int restore(unsigned int voiceType,
void *ibuf1_addr,
uint32_t ibuf1_size,
void *middle_buffer,
uint32_t middle_buffer_len,
void *save_callback)
{
// [...]
buf_off = *(uint32_t *)ibuf1_addr;
buf_ptr = ibuf1_addr + buf_off + 8;
buf_len = *(uint32_t *)(ibuf1_addr + buf_off + 4);
// [...]
}

在 中,从(其内容完全由用户控制的缓冲区)中读取。然后,此偏移值用于从 读取长度。由于我们控制 ,我们可以指向内存中的任何位置,从而触发 OOB 读取。restorebuf_offibuf1_addribuf1_addrbuf_offibuf1_addr + buf_off + 4

我们通过概念验证触发了此 bug,并获得了以下崩溃:

[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: 0xb1417145, fault_code: 0x92000005
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TEE_SERVICE_VOI] 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=8347 prefer-ca=8347
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <restore+0x94/0x248>
[HM] <RestoreTemplate>+0x2f0/0x494
[HM] <RestoreTemplate>+0x2f0/0x494
[HM] <Restore>+0x74/0x98
[HM] <TA_InvokeCommandEntryPoint>+0x98/0xe4
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM]
[HM] [TRACE][1212]pid=72 exit_status=130

空指针取消引用CheckModelHash

函数中有一个空指针取消引用:CheckModelHash

int CompareVoiceTa(uint32_t label, uint8_t *inbuf, uint32_t inbuf_size, uint8_t *outbuf) {
// [...]
CheckModelHash(g_sendGmmBuf, g_sendGmmBufTotalLen, HashCheck);
// [...]
}

int CheckModelHash(uint8_t * model_buf, uint32_t model_len, void *callback) {
// [...]
version = *(uint32_t *)(model_buf + 8);
// [...]
}

CompareVoiceTa将使用全局变量和 As 参数进行调用。这些变量应该由函数设置,但从不检查这些值是否为非零,如果未调用该命令,则会导致 NULL 指针取消引用。CheckModelHashg_sendGmmBufg_sendGmmBufTotalLenSendTaGmmBufCheckModelHashSendGmmBuf

我们通过概念验证触发了此 bug,并获得了以下崩溃:

[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: 0x8, fault_code: 0x92000006
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TEE_SERVICE_VOI] 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=8398 prefer-ca=8398
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <CheckModelHash+0x24/0xe0>
[HM] <CompareVoiceTa>+0x184/0x3a0
[HM] <CompareVoiceTa>+0x184/0x3a0
[HM] <Auth>+0x1cc/0x24c
[HM] <TA_InvokeCommandEntryPoint>+0x98/0xe4
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM]
[HM] [TRACE][1212]pid=74 exit_status=130

信息泄露compare

函数中存在信息泄漏:compare

int Auth(unsigned int paramTypes, TEE_Param params[4]) {
// [...]
plaintext = TEE_Malloc(0x100010, 0);
// [...]
CompareVoiceTa(
*(uint32_t *)plaintext,
plaintext + 4,
*(uint32_t *)(plaintext + 0x80004),
obuf1_addr);
// [...]
}

int CompareVoiceTa(uint32_t label, uint8_t *inbuf, uint32_t inbuf_size, uint8_t *outbuf) {
// [...]
templ_data = g_voiceUserDb->templates[i].data;
// [...]
float0 = 0.0;
float1 = 0.0;
// [...]
compare(label, inbuf, inbuf_size, templ_data, templ_length, &float0, &float1);
// [...]
}

int compare(
uint32_t label,
uint8_t *inbuf,
uint32_t inbuf_size,
void *templ_data,
int templ_length,
float *float0_p,
float *float1_p)
{
// [...]
if ( ... ) {
SLog(
"%s %s: parms NULL !!! %p %d %p %d %p %p \n",
"[Error]",
"compare",
inbuf,
inbuf_size,
templ_data,
templ_length,
float0_p,
float1_p);
return -1;
}
// [...]
}

中的日志字符串将泄漏 4 个指针:restore

  • inbuf,堆分配的缓冲区

  • templ_data,另一个堆分配的缓冲区

  • float0_p,指向堆栈变量的指针

  • float1_p,另一个指向堆栈变量的指针

我们通过概念验证触发了此 bug,并获得了以下输出:

[TEE_SERVICE_VOICE_REC-1] [Error] compare: parms NULL !!! 0x6bed014 0 0x6950010 360000 0x694f9dc 0x694f9e0

OOB 访问DecryptData

函数中有一个 OOB 访问:DecryptData

int DecryptDataTa(int paramTypes, TEE_Param *params) {
// [...]
if (!params[1].memref.buffer
|| params[1].memref.size - 1 >= 0x32000
|| !params[2].memref.buffer
|| params[2].memref.size != 0x16
|| !params[3].memref.buffer) {
SLog("%s: VO_TEE_DECRYPT_DATA_CMD_ID: Bad expected parameter types.\n", "[Error]");
// [...]
}
// [...]
decrypt_data.value_a = params->value.a;
decrypt_data.src = params[1];
decrypt_data.dest = params[3];
decrypt_data.IV.memref.buffer = params[2].memref.buffer;
decrypt_data.IV.memref.size = 0x10;
// [...]
DecryptData(&decrypt_data);
// [...]
}

int DecryptData(decrypt_data_t *decrypt_data) {
// [...]
size = decrypt_data->src.memref.size;
if (size > 0x32000) {
SLog("%s: bad parameter.\n", "[Error]");
// [...]
}
// [...]
dest = TEE_Malloc(size, 0);
TA_AesDecryptPKCS5(
g_aesKey,
0x10,
decrypt_data->IV.memref.buffer,
decrypt_data->IV.memref.size,
decrypt_data->src.memref.buffer,
size,
dest,
&size);
// [...]
if (size <= 4) {
SLog("%s: decrypt data len is too short.\n", "[Error]");
// [...]
}
// [...]
memmove_s(decrypt_data->dest.memref.buffer, size - 4, dest + 4, size - 4) )
// [...]
}

在 中,数据解密后,会将其复制到输出参数中。但是,用作调用的源和目标大小的大小是输出数据大小(上限为 0x32000)。从不检查输出参数的实际大小,并且可能小于输出数据大小,从而导致 OOB 写入访问。DecryptDataTEE_Parammemmove_sTEE_Param

我们通过概念验证触发了此 bug,并获得了以下崩溃:

[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: 0x70038000, fault_code: 0x92000047
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TEE_SERVICE_VOI] 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=10397 prefer-ca=10397
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] sp > fpDump task states END
[HM]
[HM] [TRACE][1212]pid=88 exit_status=130

堆缓冲区溢出SendTaGmmBuf

函数中存在堆缓冲区溢出:SendTaGmmBuf

int SendGmmBuf(uint8_t paramTypes, TEE_Param *params) {
// [...]
ibuf1_addr = params[1].memref.buffer;
ibuf1_size = params[1].memref.size;
if (params->value.a <= 0x680000 && ibuf1_addr && ibuf1_size <= 0x80000) {
params->memref.size = SendTaGmmBuf(params->value.a, ibuf1_addr, ibuf1_size);
return 0;
}
// [...]
}

int SendTaGmmBuf(uint32_t totalLen, int buf, unsigned int bufLen) {
// [...]
if (!buf || bufLen > 0x680000) {
SLog("%s: buf is NULL or bufLen is too large\n", "[Error]");
return 0x7A000003;
}

if (!g_sendGmmBufCount) {
g_sendGmmBuf = TEE_Malloc(totalLen, g_sendGmmBufCount);
// [...]
g_sendGmmBufTotalLen = totalLen;
}

memcpy_s(g_sendGmmBuf + (g_sendGmmBufCount << 0x13), bufLen, buf, bufLen);
// [...]
++g_sendGmmBufCount;
// [...]
}

CA 使用该命令将模型发送到 TA。模型以块的形式发送。当收到第一个块时,TA 将分配一个缓冲区来存储完整的模型。模型的总大小以第一个的值给出。数据块位于第二个 .SendGmmBufTEE_ParamTEE_Param

不进行任何验证以确保块大小的总和不大于模型的总大小。因此,可以为模型大小指定一个小值(例如 8),并仅用一个块(例如大小为 8)溢出分配的缓冲区(例如大小为 > 8)。

我们通过概念验证触发了此 bug,并获得了以下崩溃:

[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: 0x10e8000, fault_code: 0x92000047
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TEE_SERVICE_VOI] 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=10293 prefer-ca=10293
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] sp > fpDump task states END
[HM]
[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 2200000022 (tid: 34) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x8, fault_code: 0x92000046
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TEE_SERVICE_VOI] tid=34 is-idle=0 is-curr=0
[HM] state=BLOCKED@MEMFAULT sched.pol=0 prio=49 queued=1
[HM] aff[0]=ff
[HM] flags=1000 smc-switch=0 ca=0 prefer-ca=0
[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]

开发

在本节中,我们将演示如何利用影响 TEE_SERVICE_VOICE_REC Trustlet 的漏洞来控制执行流。为此,我们将使用函数中的信息泄漏和函数中的堆缓冲区溢出。compareSendTaGmmBuf

我们的设备设置

我们开发漏洞利用的设备是运行固件更新的P40 Pro。ELS-LGRP4-OVS_11.0.0.223

trustlet 二进制 MD5 校验和如下:

HWELS:/ # md5sum /vendor/bin/859703f3-3cc5-4e88-b263-08f9ce82e3d0.sec
2e0202c5c2a3f28c55491e1bbb44a875 /vendor/bin/859703f3-3cc5-4e88-b263-08f9ce82e3d0.sec

华为TEE OS iTrustee实现了白名单机制,只允许特定的客户端应用(原生二进制文件或APK)与可信应用通信。

在我们的例子中,TEE_SERVICE_VOICE_REC TA 只能由原生二进制文件调用:

身份验证机制分 3 个部分实现:

  • 实现 TEE 客户端 API 的守护进程检查哪个原生二进制文件/APK 正在与它通信,并将该信息发送到内核驱动程序;teecd

  • 内核驱动程序确保它正在与 通信,并将其接收到的信息转发给 TEE OS;teecd

  • TEE OS 验证客户端应用程序是否在 TA 的白名单中。

由于我们不想费心在这些二进制文件之一中注入代码,因此我们选择通过修补内核驱动程序来规避身份验证,以添加模拟任何原生二进制文件/APK 的功能。

信息泄露

要触发函数中的信息泄露,我们需要克服几个障碍:compare

  • g_voiceUserDb必须包含至少一个“模板”

  • 命令的第三个输入参数必须包含 AES 加密的数据TEE_ParamAuth

  • compare仅当模型哈希值与 2 个预期的 SHA256 哈希值之一匹配时,才会调用模型哈希值

为了克服第一个障碍,我们做了以下几点:

  • 我们发送一个命令。此函数将调用 ,该函数将设置为 0(而不是其默认值:0xffffffff)SetActiveGroupResetVoiceDB(0)g_voiceUserDb->user_id

  • 我们发送一个命令,该命令也需要AES加密的数据。此函数将向 添加一个“模板”。Enrollg_voiceUserDb

为了克服第二个障碍,我们滥用了 AES 密钥由命令初始化的事实。如果我们不调用这个函数,(位于 BSS 中)将全部为零。这使我们能够使用已知密钥加密我们的数据。g_decryptKeyGetEccPublicKeyg_decryptKey

为了克服第三个障碍,我们反转了与 trustlet () 通信的客户端应用程序,并发现可以在磁盘 中找到与 SHA256 哈希值对应的模型之一。voiceid_alg_ree.so/odm/etc/audio/voiceid/imedia/msbc_vpu/china/plda_combine_model.dat

触发易受攻击的代码路径后,我们只需解析输出,查找类似于 .logcat[TEE_SERVICE_VOICE_REC-1] [Error] compare: parms NULL !!! 0x6bed014 0 0x6950010 360000 0x694f9dc 0x694f9e0

控制流劫持

有许多方法可以利用堆缓冲区溢出。在本演示中,我们选择执行在释放对象时触发的经典取消链接堆漏洞(在命令中)。此技术允许在内存中的任意位置写入受控的 dword 值。g_sendGmmBufRelease

通过定位保存在堆栈上的链路寄存器,我们可以劫持 Trustlet 的执行流。我们计算了 的局部变量和 的保存链路寄存器之间的堆栈偏移量。然后,我们在结果地址上写入任意值,并观察到控制流被重定向。float0CompareVoiceTaTEE_FreeReleaseTa

在漏洞利用中,我们使用 0x70000000,因为我们只能使用取消链接技术写入表示可写地址的值,但应该可以使用类似于我们在TEE_SERVICE_MULTIDRM漏洞中使用的技术来构造不受限制的任意写入。

以下是漏洞利用代码的输出,显示了泄露的堆栈地址:

adb wait-for-device shell su root sh -c "/data/local/tmp/tee_service_voice_rec"
stack_addr = 6b969dc

以及当控制流重定向时立即发生的崩溃:

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

受影响的设备

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

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

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

补丁

名字严厉CVE漏洞补丁
OOB 访问DecryptData危急CVE-2021-40036 漏洞-2021-400362022 年 1 月
堆缓冲区溢出SendTaGmmBuf危急CVE-2021-40010 漏洞2022 年 1 月
OOB 访问restoreCVE-2021-40027 漏洞2022 年 1 月
信息泄露compare中等CVE-2021-40032 漏洞-2021-400322022 年 1 月
信息泄露restore中等CVE-2021-40014 漏洞2022 年 1 月
空指针取消引用CheckModelHash不适用固定

时间线

  • 2021年11月09日 - 向华为PSIRT发送漏洞报告。

  • 2021年11月22日 - 华为PSIRT确认该漏洞报告。

  • 2022年1月1日 - 华为PSIRT表示,这些问题已在2022年1月的更新中修复。

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

  • 2023年6月13日 - 我们通知华为PSIRT,部分漏洞未修复。

  • 2023年6月20日 - 华为PSIRT回复,将在2023年7月更新中修复。

二进制漏洞(更新中)

其它课程

windows网络安全防火墙与虚拟网卡(更新完成)

windows文件过滤(更新完成)

USB过滤(更新完成)

游戏安全(更新中)

ios逆向

windbg

恶意软件开发(更新中)

还有很多免费教程(限学员)

更多详细内容添加作者微信


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