二进制漏洞分析-10.华为TrustZone TEE_SERVICE_VOICE_REC漏洞

二进制漏洞分析-10.华为TrustZone TEE_SERVICE_VOICE_REC漏洞

原创 haidragon 安全狗的自我修养 2023-11-25 07:13

华为TrustZone TEE_SERVICE_VOICE_REC漏洞

二进制漏洞分析-1.华为Security Hypervisor漏洞

二进制漏洞分析-2.揭示华为安全管理程序(上)

二进制漏洞分析-3.揭示华为安全管理程序(下)

二进制漏洞分析-4.华为安全监控漏洞(SMC SE 工厂检查 OOB 访问)

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

二进制漏洞分析-6.Parallels Desktop Toolgate 漏洞

二进制漏洞分析-7.华为TrustZone Vsim_Sw漏洞

二进制漏洞分析-8.Huawei TrustZone VprTa漏洞

二进制漏洞分析-9.华为TrustZone TEE_Weaver漏洞

此通报包含有关以下漏洞的信息:
– 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 只能由原生二进制文件调用:
– /vendor/bin/hw/[email protected]
(1000 元)

身份验证机制分 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-40036 2022 年 1 月
堆缓冲区溢出SendTaGmmBuf 危急 CVE-2021-40010 漏洞 2022 年 1 月
OOB 访问restore CVE-2021-40027 漏洞 2022 年 1 月
信息泄露compare 中等 CVE-2021-40032 漏洞-2021-40032 2022 年 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

恶意软件开发(更新中)

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

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