二进制漏洞分析-12.华为TrustZone TEE_SERVICE_MULTIDRM漏洞(下)

admin 2023年11月28日13:06:14评论20 views字数 10882阅读36分16秒阅读模式

二进制漏洞分析-12.华为TrustZone TEE_SERVICE_MULTIDRM漏洞(下)

 

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

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

二进制漏洞分析-11.华为TrustZone TEE_SERVICE_MULTIDRM漏洞(上)

二进制漏洞分析-11.华为TrustZone TEE_SERVICE_MULTIDRM漏洞(上)

OOB 读取访问权限MDrm_TA_CMD_OEMCrypto_DecryptCENC

第一个 OOB 读取访问权限位于以下函数中:MDrm_TA_CMD_OEMCrypto_DecryptCENC

int MDrm_TA_CMD_OEMCrypto_DecryptCENC(int paramTypes, TEE_Param params[4]) {
// [...]
cur_ptr = params[0].memref.buffer;
size_left = params[0].memref.size;
// [...]

ret = MDrm_Utils_ReadU32(cur_ptr, size_left, &user1);
if (ret) goto EXIT;
cur_ptr += 4; size_left -= 4;

ret = MDrm_Utils_ReadU16(cur_ptr, size_left, &user2);
if (ret) goto EXIT;
cur_ptr += 2; size_left -= 2;

ret = MDrm_Utils_ReadU32(cur_ptr, size_left, &user3);
if (ret) goto EXIT;
cur_ptr += 4 + user3;
size_left -= 4 + user3;

ret = MDrm_Utils_ReadU32(cur_ptr, size_left, &user4);
// [...]
}

如上面的代码片段所示,用户控制的值用于递增 和 递减。如果给出的值大于缓冲区的实际剩余大小,则缓冲区将越界。上还会有一个整数下溢,导致巨大的尺寸。下一次调用将从攻击者控制的地址 () 读取。user3cur_ptrsize_leftuser3params[0]cur_ptrsize_leftMDrm_Utils_ReadU320x7000300a + user3

我们通过概念验证触发了此错误,并在以下位置获得了以下崩溃:0xb141714b = 0x7000300a + 0x41414141

[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: 0xb141714b, fault_code: 0x92000005
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TEE_SERVICE_MUL] 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=7364 prefer-ca=7364
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <MDrm_Utils_ReadU32+0x48/0x6c>
[HM] <MDrm_TA_CMD_OEMCrypto_DecryptCENC>+0xfc/0x300
[HM] <MDrm_TA_CMD_OEMCrypto_DecryptCENC>+0xfc/0x300
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM]
[HM] [TRACE][1212]pid=52 exit_status=130

OOB 读取访问权限MDrm_TA_CMD_OEMCrypto_RewrapDeviceRSAKey30

可以在函数中找到类似的 OOB 读取访问权限:MDrm_TA_CMD_OEMCrypto_RewrapDeviceRSAKey30

int MDrm_TA_CMD_OEMCrypto_RewrapDeviceRSAKey30(int paramTypes, TEE_Param params[4]) {
// [...]
cur_ptr = params[0].memref.buffer;
size_left = params[0].memref.size;
// [...]

// 2 calls to MDrm_Utils_ReadU32
// [...]

ret = MDrm_Utils_ReadU32(cur_ptr, size_left, &read_val);
if (ret) goto EXIT;
cur_ptr += 4 + read_val;
size_left -= 4 + read_val;

ret = MDrm_Utils_ReadU32(cur_ptr, size_left, &read_val);
// [...]
}

根本原因与上一个 bug 完全相同。我们通过概念验证触发了此错误,并在以下位置获得了以下崩溃:0xb141714d = 0x7000300c + 0x41414141

[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: 0xb141714d, fault_code: 0x92000005
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TEE_SERVICE_MUL] 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=6821 prefer-ca=6821
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <MDrm_Utils_ReadU32+0x48/0x6c>
[HM] <MDrm_TA_CMD_OEMCrypto_RewrapDeviceRSAKey30>+0xd8/0x18c
[HM] <MDrm_TA_CMD_OEMCrypto_RewrapDeviceRSAKey30>+0xd8/0x18c
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM]
[HM] [TRACE][1212]pid=42 exit_status=130

OOB 读取访问权限MDrm_TA_CMD_Provision_GetRequest

可以在函数中找到类似的 OOB 读取访问权限:MDrm_TA_CMD_Provision_GetRequest

int MDrm_TA_CMD_Provision_GetRequest(int paramTypes, TEE_Param params[4]) {
// [...]
cur_ptr = params[0].memref.buffer;
size_left = params[0].memref.size;
// [...]

ret = MDrm_Utils_ReadU32(cur_ptr, size_left, &read_val);
if (ret) goto EXIT;
cur_ptr += 4 + read_val;
size_left -= 4 + read_val;

ret = MDrm_Utils_ReadU32(cur_ptr, size_left, &read_val);
// [...]
}

根本原因与上一个 bug 完全相同。我们通过概念验证触发了此错误,并在以下位置获得了以下崩溃:0xb1417145 = 0x70003004 + 0x41414141

[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_MUL] 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=5746 prefer-ca=5746
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <MDrm_Utils_ReadU32+0x48/0x6c>
[HM] <MDrm_TA_CMD_Provision_GetRequest>+0x94/0x158
[HM] <MDrm_TA_CMD_Provision_GetRequest>+0x94/0x158
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM]

开发

如漏洞详细信息部分所述,我们将利用其中一个漏洞,即 中的漏洞,以证明可以从每个堆缓冲区溢出中获得任意读/写。其他溢出的利用路径将非常相似。MDrm_TA_CMD_OEMCrypto_LoadEntitledContentKeys

我们的设备设置

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

trustlet 二进制 MD5 校验和如下:

HWELS:/ # md5sum /vendor/bin/0069f244-9733-4e8c-98c7-e36cb764a6b6.sec
eb62634a90b7e72da420d124019cc13b /vendor/bin/0069f244-9733-4e8c-98c7-e36cb764a6b6.sec

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

在我们的例子中,TEE_SERVICE_MULTIDRM TA 只能被 4 个原生二进制文件调用:

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

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

任意读取

第一步是构建一个任意读取,因为我们可以使用它来查找 trustlet 的基址(使用 TALoader 信息泄漏)。

为了构建这个任意读取,我们将把我们可以溢出的对象放在会话之前。这样,我们将能够修改会话对象的字段,特别是在某些命令中取消引用的“RSA 键指针”。

为了确保我们的分配最终到达我们想要的位置,我们需要做一些堆整形。在我们的漏洞利用中,我们首先分配 64 个会话(最大数量)。这些会话应该在内存中相互跟随。然后我们释放其中一个,在我们的例子中是中间的那个,为我们将分配和溢出的对象留出空间。

它实际上比这要复杂一些,因为打开单个会话会触发 3 个分配:

  • 分配0x98(四舍五入至0xA0,会议)

  • 0x30分配(四舍五入为 0x40,随机数表条目)

  • 分配0x8(四舍五入到0x10,列表条目)

尽管如此,这 3 次分配可以被认为是一个大分配。它们在关闭会话时按顺序释放,分配器会合并相邻对象,因此它们最终将作为大小为 0xF0 的单个可用块。

int MDrm_TA_CMD_OEMCrypto_LoadEntitledContentKeys(int paramTypes, TEE_Param params[4]) {
// [...]
cur_ptr = params[0].memref.buffer;
size_left = params[0].memref.size;
uint32_t *alloc;
// [...]

ret = MDrm_Utils_ReadU32(cur_ptr, size_left, &user1);
if (ret) goto EXIT;
cur_ptr += 4; size_left -= 4;

ret = MDrm_Utils_ReadU32(cur_ptr, size_left, &user2);
if (ret) goto EXIT;
cur_ptr += 4; size_left -= 4;

ret = MDrm_Memory_Calloc(8 * sizeof(uint32_t) * user2, &alloc);
if (ret) goto EXIT;

for (int i = 0; i != user2; ++i) {
for (int j = 0; j != 8; ++j) {
ret = MDrm_Utils_ReadU32(cur_ptr, size_left, &alloc[i*8+j]);
if (ret) goto EXIT;
cur_ptr += 4; size_left -= 4;
}
}

ret = MDrm_TA_CryptoKeyLadder_LoadEntitledContentKeys(user1, &params[1], user2, alloc);

EXIT:
MDrm_free(alloc);
return ret;
}

在喷洒堆后,我们调用分配和溢出对象。由于我们指定的值为 0x80000007,因此分配的大小应完全适合我们在堆喷涂期间创建的孔(为堆元数据留出足够的空间)。LoadEntitledContentKeysuser20x80000007 * 0x18 = 0xE0

在我们溢出的对象之后是下一个对象的元数据:

struct chunk {
size_t psize, csize; /* for all objects */
struct chunk *next, *prev; /* for free objects only */
};

在会话对象的元数据中,我们覆盖:

  • 的 .0xF0 是我们刚刚分配的对象的大小(考虑到元数据大小,并四舍五入到下一个 16 字节边界)。1 是标志(表示前一个对象已分配且未释放)。这可以防止在释放对象时崩溃,因为分配器可以检测到损坏的页脚。psize0xF1 = 0xF0 | 1C_INUSE

  • 的 .0xA0 是会话对象(打开会话时分配的 3 个对象中的第一个)的大小。我们指定标志,这样它就不会与我们的对象合并。csize0xA1 = 0xA0 | 1C_INUSE

由于会话对象正在使用中,因此 和 指针不在其元数据中,并且对象内容紧跟在 之后。nextprevcsize

在会话对象的内容中,我们覆盖:

  • 会话 ID(偏移量为 0 的字段)0x1337;

  • 指向会话的 RSA 密钥(位于偏移量 4)的指针,其中包含我们要从中读取的地址;

  • RSA 密钥用法(偏移量 8)和 0xFF(用于传递检查)。GenerateRSASignature

然后,我们使用该命令触发任意读取。GenerateRSASignature

int MDrm_TA_CMD_OEMCrypto_GenerateRSASignature(int paramTypes, TEE_Param params[4]) {
// [...]
ret = MDrm_TA_DrmCertificateProvisioning_GenerateRSASignature(
params[0].value.a, &params[1], &params[2], params->value.b);
if (ret == 0x77770007) {
ret = 0;
params[3].value.a = params[2].memref.size;
}
// [...]
}
int MDrm_TA_DrmCertificateProvisioning_GenerateRSASignature(int sessionId, TEE_Param *data TEE_Param *sign, int usage) {
// [...]
ret = MDrm_TA_OEMCryptoEngine_FindSession(sessionId, &session);
if (!ret)
ret = MDrm_TA_OEMCryptoSession_SignMessage(session, data, sign, usage);
return ret;
}
int MDrm_TA_OEMCryptoSession_SignMessage(session_t *session, TEE_Param *data, TEE_Param *sign, int usage) {
// [...]
if ((usage & session->rsa_key_usage) == 0)
return 0x77770019;
ret = MDrm_TA_Crypto_RsaKeySze(rsa_key, &rsa_key_size);
if (!ret) {
if (sign->memref.size < rsa_key_size) {
result = 0x77770007;
sign->memref.size = rsa_key_size;
}
// [...]
}
return ret;
}
int MDrm_TA_Crypto_RsaKeySze(void *key, uint32_t *key_size_p) {
// [...]
*key_size_p = *(key + 4);
// [...]
}

此命令可以返回从我们刚刚伪造的 RSA 键指针读取的值。我们设置为 0xFF 的 RSA 密钥用法字段允许传递第一次签入。然后,此函数调用 ,该函数将从 RSA 键指针读取偏移量 4 处的 dword,并将其放入局部变量 中。通过指定一个非常小的值(其中一个输入参数),将设置为,并且该函数将提前返回代码0x77770007。MDrm_TA_OEMCryptoSession_SignMessageMDrm_TA_Crypto_RsaKeySzersa_key_sizesign->memref.sizesign->memref.sizersa_key_size

返回 ,将检查 的返回码,如果0x77770007,则将设置为放入 的值。这有效地允许 CA 检索刚刚读取的值。MDrm_TA_CMD_OEMCrypto_GenerateRSASignatureMDrm_TA_DrmCertificateProvisioning_GenerateRSASignatureparams[3].value.asign->memref.size

为了避免在会话关闭时崩溃,每次读取后,我们都会再次触发堆缓冲区溢出,以将 RSA 键指针设置为 NULL。

任意写入

对于我们的 write 原语,我们可以简单地使用经典的取消链接堆利用技术,当我们的对象被释放时触发。

// called from free(), c is the freed object
static void unbin(struct chunk *c, int i)
{
if (c->prev == c->next)
a_and_64(&mal.binmap, ~(1ULL<<i));
c->prev->next = c->next;
c->next->prev = c->prev;
c->csize |= C_INUSE;
NEXT_CHUNK(c)->psize |= C_INUSE;
}

通过溢出下一个对象的元数据,我们可以将 和 设置为任意值。释放此对象时,将执行该函数,它将:c->prevc->nextunbin

  • 将值写入地址c->nextc->prev + 8;

  • 将值写入地址。c->prevc->next + 12

但这种技术有一个主要缺点:地址和值都必须是可写的地址。为了解决这个问题,我们决定使用该命令进行最后的写入。SetDecryptHash

int MDrm_TA_CMD_OEMCrypto_SetDecryptHash(int paramTypes, TEE_Param params[4]) {
// [...]
return MDrm_TA_TestAndVerificationFunctions_SetDecryptHash(
params[0].value.a, params[0].value.b, &params[1]);
}
int MDrm_TA_TestAndVerificationFunctions_SetDecryptHash(int a, int b, TEE_Param *param1) {
// [...]
ret = j_MDrm_TA_OEMCryptoEngine_FindSession(a, &session);
if (!ret)
ret = MDrm_TA_OEMCryptoSession_SetDecryptHash(session, b, param1);
return ret;
}
int MDrm_TA_OEMCryptoSession_SetDecryptHash(session_t *session, int b, TEE_Param *param1) {
// [...]
session->user_b = b; /* offset 0x8c */
session->user_param1 = *(int *)param1->memref.buffer; /* offset 0x88 */
// [...]
}

此命令将在会话对象中的偏移量 0x88 和 0x8c处写入两个 dword 值。我们需要给它一个位于用户控制地址的会话对象。为此,我们可以使用 unlink 原语修改全局会话链表,该链表可按会话的 ID 查找会话。MDrm_TA_OEMCryptoEngine_FindSession

在我们的漏洞利用中,我们通过在 trustlet 部分写入一个值 (0xdeadbeef) 并将其读回来演示我们的读/写原语。.data

adb wait-for-device shell su root sh -c "/data/local/tmp/tee_service_multidrm"
ta_base_addr = 38fb000
g_sessions = 113ef10
first = 113ffb0
0392c0d0: deadbeef (should be 0xdeadbeef)

获取代码执行

虽然它不包含在漏洞中,但可以执行代码,例如通过覆盖 cJSON 库的全局变量的函数指针或 libc 的众多全局变量之一。这可用于进行任意系统调用,并可能进一步提升权限。global_hooks

受影响的设备

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

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

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

补丁

名字 严厉 CVE漏洞 补丁
堆缓冲区溢出MDrm_TA_CMD_OEMCrypto_LoadKeys 危急 CVE-2021-46881 漏洞 2023 年 5 月
堆缓冲区溢出MDrm_TA_CMD_OEMCrypto_LoadEntitledContentKeys 危急 CVE-2021-40034 漏洞 2022 年 8 月
堆缓冲区溢出MDrm_TA_CMD_OEMCrypto_RefreshKeys 危急 CVE-2021-46882 漏洞-2021-46882 2023 年 5 月
堆缓冲区溢出MDrm_TA_OEMCryptoUsageTable_LoadUsageTableHeader 危急 CVE-2021-46883 漏洞CVE-2021-46883 2023 年 5 月
OOB 写入访问权限MDrm_TA_CMD_OEMCrypto_CopyBuffer 危急 CVE-2021-46884 漏洞 2023 年 5 月
OOB 读取访问权限MDrm_TA_CMD_Provision_GetRequest CVE-2021-46885 漏洞CVE-2021-46885 2023 年 5 月
OOB 读取访问权限MDrm_TA_CMD_OEMCrypto_RewrapDeviceRSAKey30 CVE-2021-46886 漏洞 2023 年 5 月
OOB 读取访问权限MDrm_TA_CMD_OEMCrypto_DecryptCENC CVE-2021-46814 漏洞 2022 年 6 月

时间线

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

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

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

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

二进制漏洞(更新中)

二进制漏洞分析-12.华为TrustZone TEE_SERVICE_MULTIDRM漏洞(下)

其它课程

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


二进制漏洞分析-12.华为TrustZone TEE_SERVICE_MULTIDRM漏洞(下)

windows文件过滤(更新完成)

二进制漏洞分析-12.华为TrustZone TEE_SERVICE_MULTIDRM漏洞(下)

USB过滤(更新完成)

二进制漏洞分析-12.华为TrustZone TEE_SERVICE_MULTIDRM漏洞(下)

游戏安全(更新中)

二进制漏洞分析-12.华为TrustZone TEE_SERVICE_MULTIDRM漏洞(下)


ios逆向

二进制漏洞分析-12.华为TrustZone TEE_SERVICE_MULTIDRM漏洞(下)

windbg

二进制漏洞分析-12.华为TrustZone TEE_SERVICE_MULTIDRM漏洞(下)

恶意软件开发(更新中)

二进制漏洞分析-12.华为TrustZone TEE_SERVICE_MULTIDRM漏洞(下)


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

二进制漏洞分析-12.华为TrustZone TEE_SERVICE_MULTIDRM漏洞(下)

二进制漏洞分析-12.华为TrustZone TEE_SERVICE_MULTIDRM漏洞(下)

二进制漏洞分析-12.华为TrustZone TEE_SERVICE_MULTIDRM漏洞(下)




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


二进制漏洞分析-12.华为TrustZone TEE_SERVICE_MULTIDRM漏洞(下)

 

 

二进制漏洞分析-12.华为TrustZone TEE_SERVICE_MULTIDRM漏洞(下)





原文始发于微信公众号(安全狗的自我修养):二进制漏洞分析-12.华为TrustZone TEE_SERVICE_MULTIDRM漏洞(下)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年11月28日13:06:14
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   二进制漏洞分析-12.华为TrustZone TEE_SERVICE_MULTIDRM漏洞(下)https://cn-sec.com/archives/2246878.html

发表评论

匿名网友 填写信息