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

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

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

华为TrustZone TEE_Weaver漏洞 

二进制漏洞分析-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漏洞

此通报包含有关以下漏洞的信息:
– CVE-2021-40022 漏洞 InterfaceRead 中缺少输入参数检查

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

此 TA 实现了 5 个命令:、、和 。GetConfigInterfaceWriteInterfaceReadGetStatusCollectRetryErr

信息泄露¶

有 3 次对 in 的调用,这会导致 中的信息泄露。这些基元可用于泄漏堆栈地址、缓冲区地址,从而泄漏受信任应用程序的 ASLR 幻灯片。SLogInterfaceWriteInterfaceReadlogcat

第一个调用在函数中:Read

int Read(TEE_Param *params, unsigned int index) {
    // [...]
    int responseLen;
    char response[0x108];
    // [...]
    buffer1 = params[1].memref.buffer;
    length1 = params[1].memref.length;
    // [...]
    SLog("%s: DoRead(%u, %p, %u, %u, %u)#%u\n", "[Trace]", slotId, buffer1, length1, response, responseLen, retry);
    // [...]
}

此命令的参数是一个“值”(在索引 0 处)和两个“memrefs”(在索引 1 和 2 处)。日志字符串将泄漏映射第一个缓冲区的地址,以及堆栈上分配的缓冲区的地址:

[TEE_WEAVER-1] [Trace]: DoRead(63, 0x70003000, 16, 59722052, 261)#0

第二个调用在函数中,在我们的例子中,该函数已内联在函数中:CheckReadRespDoRead

int DoRead(int slotId, int buffer1, int length1, unsigned __int8 *response, int *responseLen) {
    // [...]
    SLog("%s: %s,WEAVER_RSP: response value %p, len=%u, rsp[0]=%02x, sw1,sw2=%02x %02x\n",
         "[Trace]", "CheckReadResp", response, *responseLen, *response, 0x90, 0);
    // [...]
}

日志字符串将泄漏堆栈上分配的相同缓冲区的地址:

[TEE_WEAVER-1] [Trace]: CheckReadResp,WEAVER_RSP: response value 0x38f4944, len=23, rsp[0]=00, sw1,sw2=90 00

第三次调用在函数中:IsResponseError

int IsResponseError(unsigned __int8 *response, unsigned int responseLen) {
    // [...]
    SLog("%s: %s,response value %p, len=%u, %02x %02x\n",
         "[Trace]", "IsResponseError", response, responseLen, *response, response[1]);
    // [...]
}

unsigned int DoWrite(unsigned int slotId, int buffer1, int length1, int buffer2, int length2) {
    // [...]
    unsigned int responseLen;
    // [...]
    unsigned __int8 response[0x130];
    // [...]
    IsResponseError(response, responseLen);
    // [...]
}

日志字符串将泄漏另一个堆栈分配缓冲区的地址:

[TEE_WEAVER-1] [Trace]: IsResponseError,response value 0x38f4904, len=2, 90 00

缺少输入参数签入InterfaceRead¶

未正确完成命令的参数类型验证,这允许将用户控制的数据写入受信任应用程序地址空间内的任意地址。InterfaceRead

int cmd_iface_read_check(int paramTypes, TEE_Param *params) {
    // [...]
    if ((paramTypes & 0xF) == TEE_PARAM_TYPE_VALUE_INPUT
            && ((paramTypes >> 4) & 0xF) == TEE_PARAM_TYPE_MEMREF_INPUT) {
        if (!IsValidSlotId(params[0].value.a)
                && params[1].memref.buffer
                && params[1].memref.length <= 0x10) {
            return 0;
        } else {
            SLog("%s: Check interface read input parameter has problem.\n", "[Error]");
            return 2;
        }
    } else {
        SLog("%s: Bad expected parameter types: 0x%X.\n", "[Error]", paramTypes);
        return 3;
    }
}

验证函数不检查第三个参数的类型,该参数应该是 .这意味着我们可以给它一个。TEE_PARAM_TYPE_MEMREF_OUTPUTTEE_PARAM_TYPE_VALUE_INPUT

typedef union {
    struct {
        void *buffer;
        uint32_t size;
    } memref;
    struct {
        uint32_t a;
        uint32_t b;
    } value;
} TEE_Param;

由于字段 和 分别与 和 重叠,因此我们可以使 TA 写入 指定的用户控制地址,而不是写入输出缓冲区。输出缓冲区由函数中的命令使用:memref.buffermemref.sizevalue.avalue.bvalue.aInterfaceReadReturnReadResult

int ReturnReadResult(TEE_Param *params, unsigned __int8 *response, int responseLen) {
    // [...]
    buffer2 = params[2].memref.buffer; // overlapping with params[2].value.a
    length2 = params[2].memref.length; // overlapping with params[2].value.b
    // [...]
    memcpy_s(buffer2, length2, &response[1], 0x10);
    // [...]
}

触发类型混淆将导致将 0x10 字节(来自安全元件的响应)写入我们选择的地址。我们还需要确保大于0x10否则会提前返回。bmemcpy_s

这是指定无效写入地址(例如0xdeadbeef)时得到的崩溃报告:

[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: 0xdeadbeef, fault_code: 0x92000045
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM]     name=[TEE_WEAVER] 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=5855 prefer-ca=5855
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <memcpy_s>+0x60/0x6c
[HM] invalid fp. backtrace abort
[HM] Dump task states END
[HM]
[HM] [TRACE][1212]pid=39 exit_status=130

开发¶

我们的设备设置¶

我们开发漏洞利用的设备是运行固件更新的P40 Pro。trustlet 二进制 MD5 校验和如下:ELS-LGRP4-OVS_11.0.0.196

HWELS:/ # md5sum /vendor/bin/42abc5f0-2d2e-4c3d-8c3f-3499783ca973.sec
b1500b3c77250dc1c1e2e9b829b0de5b  /vendor/bin/42abc5f0-2d2e-4c3d-8c3f-3499783ca973.sec

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

在我们的例子中,TEE_Weaver TA 只能由 3 个原生二进制文件调用:
– /vendor/bin/hw/vendor.huawei.hardware.hwsecurity-service
(1000 元);

  • /vendor/bin/atcmdserver
    (uid 0);

  • /vendor/bin/hw/[email protected]
    (uid 1000)。

身份验证机制分为 3 个部分:- 守护进程,实现 TEE 客户端 API,检查哪个原生二进制文件/APK 正在与它通信,并将该信息发送到内核驱动程序;- 内核驱动程序确保它正在与 通信,并将它收到的信息转发给 TEE OS;- TEE OS 验证客户端应用是否在 TA 的白名单中。teecdteecd

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

泄漏 ASLR 幻灯片¶

为了泄露 trustlet 的基址,我们在命令中使用了日志字符串。我们可以通过发送写入命令来触发此代码路径,以在未使用的插槽(此处为插槽 #63)中设置 weaver 令牌的值,然后发送同一插槽的读取命令以将其读回。CheckReadResp

剩下要做的就是解析命令的输出。我们执行它,过滤掉不是来自 的消息,然后查找字符串。logcatteeos-TEE_WEAVERresponse value

写入/读取 Trustlet 存储器¶

触发任意写入是通过发送读取命令完成的。但写入用户控制地址的值是 weaver 令牌。我们可以通过首先发送写入命令来设置其值来控制它。为了不干扰系统,我们再次将未使用的插槽(插槽 63)作为目标。

我们可以通过禁用 write 命令的参数类型检查来从任意写入创建任意读取。这是通过覆盖 commands 数组中的函数指针来完成的。

cmd_t cmds[] = {
    {0, cmd_get_config_init,  cmd_get_config_check,  cmd_get_config_proc},
    {1, cmd_iface_write_init, cmd_iface_write_check, cmd_iface_write_proc},
    {2, cmd_iface_read_init,  cmd_iface_read_check,  cmd_iface_read_proc},
    {3, cmd_get_status_init,  cmd_get_status_check,  cmd_get_status_proc},
    {8, cmd_collect_err_init, cmd_collect_err_check, cmd_collect_err_proc},
};

指向的指针将替换为指向 的指针。此命令不执行任何验证:cmd_iface_write_checkcmd_collect_err_check

int cmd_collect_error_check() {
    return 0;
}

现在,如果我们使用“value”而不是“memref”作为读取 weaver 令牌的参数,则 trustlet 将从用户控制的地址复制数据。要检索复制的数据,我们可以在同一插槽上发送读取命令。

在漏洞利用中,我们使用我们刚刚创建的任意读取原语来转储 trustlet 内存:

adb wait-for-device shell su root sh -c "/data/local/tmp/tee_weaver"
Base address: 03c1a000
TA memory dump:
0x03c1a000: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 .ELF............
0x03c1a010: 03 00 28 00 01 00 00 00 74 0f 00 00 34 00 00 00 ..(.....t...4...
0x03c1a020: 80 91 00 00 00 02 00 05 34 00 20 00 05 00 28 00 ........4. ...(.
0x03c1a030: 10 00 0f 00 01 00 00 00 00 00 00 00 00 00 00 00 ................
0x03c1a040: 00 00 00 00 91 7f 00 00 91 7f 00 00 05 00 00 00 ................
...

获取代码执行¶

虽然上面的漏洞中没有详细说明,但可以通过替换函数指针/覆盖返回地址和链接 ROP 小工具来执行代码。有很多小工具可供选择,因为共享库(、、等)映射在 trustlet 的地址空间中。libc_shared_a32.solibtee_shared_a32.solibvendor_shared_a32.so

例如,这可用于进行任意系统调用,并可能进一步提升权限。

受影响的设备¶

我们验证了这些漏洞是否影响了以下设备:
– 麒麟990:P40 专业版 (ELS)

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

补丁¶

名字 严厉 CVE漏洞 补丁
缺少输入参数签入InterfaceRead 危急 CVE-2021-40022 漏洞 2022 年 1 月

时间线¶

  • 2021年10月04日 – 向华为PSIRT发送漏洞报告。

  • 2021 年 10 月 4 日 – 报告勘误表更正了受信任的应用程序哈希,已发送给华为 PSIRT。

  • 2021年10月25日 – 华为PSIRT确认该漏洞报告。

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

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

二进制漏洞(更新中)

其它课程

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

windows文件过滤(更新完成)

USB过滤(更新完成)

游戏安全(更新中)

ios逆向

windbg

恶意软件开发(更新中)

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

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