二进制漏洞分析-5.华为安全监控漏洞(SMC MNTN OOB 访问)
二进制漏洞分析-10.华为TrustZone TEE_SERVICE_VOICE_REC漏洞
此通报包含有关以下漏洞的信息:
CVE-2021-46881 漏洞 MDrm_TA_CMD_OEMCrypto_LoadKeys中的堆缓冲区溢出
CVE-2021-40034 漏洞 MDrm_TA_CMD_OEMCrypto_LoadEntitledContentKeys中的堆缓冲区溢出
CVE-2021-46882 漏洞-2021-46882 MDrm_TA_CMD_OEMCrypto_RefreshKeys中的堆缓冲区溢出
CVE-2021-46883 漏洞CVE-2021-46883 MDrm_TA_OEMCryptoUsageTable_LoadUsageTableHeader中的堆缓冲区溢出
CVE-2021-46884 漏洞 MDrm_TA_CMD_OEMCrypto_CopyBuffer中的 OOB 写入访问权限
CVE-2021-46885 漏洞CVE-2021-46885 MDrm_TA_CMD_Provision_GetRequest中的 OOB 读取访问权限
CVE-2021-46886 漏洞 MDrm_TA_CMD_OEMCrypto_RewrapDeviceRSAKey30中的 OOB 读取访问权限
CVE-2021-46814 漏洞 MDrm_TA_CMD_OEMCrypto_DecryptCENC中的 OOB 读取访问权限
华为TEE_SERVICE_MULTIDRM TA符合GlobalPlatform TEE Internal Core API。因此,它从位于正常环境中的客户端应用程序接收命令。这些命令由函数处理。TA_InvokeCommandEntryPoint
MDrm_TA_CMD_OEMCrypto_LoadEntitledContentKeys
¶第一个堆缓冲区溢出位于以下函数中:MDrm_TA_CMD_OEMCrypto_LoadEntitledContentKeys
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, ¶ms[1], user2, alloc);
EXIT:
MDrm_free(alloc);
return ret;
}
调用 的第一个参数上存在整数溢出。是来自 中的缓冲区的用户控制值。例如,通过给出值,分配的大小将为 。MDrm_Memory_Calloc
user2
params[0]
user2
0x80000001
0x80000001 * 0x20 = 0x20
user2
然后用作循环计数器。循环的每次迭代都会从缓冲区读取 8 个整数值,并将其读取到堆分配的缓冲区中。由于分配的缓冲区小于预期,这允许攻击者使用受控数据溢出缓冲区。此外,还可以通过限制缓冲区的大小来控制溢出大小,当到达缓冲区末尾时,将返回错误代码。params[0]
params[0]
MDrm_Utils_ReadU32
我们通过概念验证触发了此 bug,并获得了以下崩溃:
[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1f00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x1032000, fault_code: 0x92000047
[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=7015 prefer-ca=7015
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <MDrm_TA_CMD_OEMCrypto_LoadEntitledContentKeys+0x19c/0x27c>
[HM] <MDrm_TA_CMD_OEMCrypto_LoadEntitledContentKeys>+0x174/0x27c
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM]
[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1f00000022 (tid: 34) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x41414149, fault_code: 0x92000046
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TEE_SERVICE_MUL] 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]
可以利用此堆缓冲区溢出来获取任意读取和写入功能,我们将在本通报的“利用利用”部分中演示。
MDrm_TA_CMD_OEMCrypto_LoadKeys
¶可以在函数中找到类似的堆缓冲区溢出:MDrm_TA_CMD_OEMCrypto_LoadKeys
int MDrm_TA_CMD_OEMCrypto_LoadKeys(int paramTypes, TEE_Param params[4]) {
// [...]
cur_ptr = params[0].memref.buffer;
size_left = params[0].memref.size;
uint32_t *alloc;
// [...] // 5 calls to MDrm_Utils_ReadU32
// [...]
ret = MDrm_Utils_ReadU32(cur_ptr, size_left, &read_val);
if (ret) goto EXIT;
cur_ptr += 4; size_left -= 4;
ret = MDrm_Memory_Calloc(10 * sizeof(uint32_t) * read_val, alloc);
if (ret) goto EXIT;
for (i = 0; i != read_val; ++i) {
for (int j = 0; j != 10; ++j) {
ret = MDrm_Utils_ReadU32(cur_ptr, size_left, &alloc[i*10+j]);
if (ret) goto EXIT;
cur_ptr += 4; size_left -= 4;
}
}
// 5 calls to MDrm_Utils_ReadU32
// [...]
ret = MDrm_TA_CryptoKeyLadder_LoadKeys(...);
EXIT:
MDrm_free(alloc);
return ret;
}
根本原因与上一个 bug 完全相同。例如,通过赋予值 0x6666667,分配的大小将为 。read_val
0x6666667 * 0x28 = 0x18
我们通过概念验证触发了此 bug,并获得了以下崩溃:
[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1f00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x1281000, fault_code: 0x92000047
[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=7265 prefer-ca=7265
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <MDrm_TA_CMD_OEMCrypto_LoadKeys+0x2d8/0x484>
[HM] <MDrm_TA_CMD_OEMCrypto_LoadKeys>+0x2b4/0x484
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM]
[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1f00000022 (tid: 34) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x41414149, fault_code: 0x92000046
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TEE_SERVICE_MUL] 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]
MDrm_TA_CMD_OEMCrypto_RefreshKeys
¶可以在函数中找到类似的堆缓冲区溢出:MDrm_TA_CMD_OEMCrypto_RefreshKeys
int MDrm_TA_CMD_OEMCrypto_RefreshKeys(int paramTypes, TEE_Param params[4]) {
// [...]
cur_ptr = params[0].memref.buffer;
size_left = params[0].memref.size;
uint32_t *alloc;
// [...] // 1 call to MDrm_Utils_ReadU32
// [...]
ret = MDrm_Utils_ReadU32(cur_ptr, size_left, &read_val);
if (ret) goto EXIT;
cur_ptr += 4; size_left -= 4;
ret = MDrm_Memory_Calloc(6 * sizeof(uint32_t) * read_val, alloc);
if (ret) goto EXIT;
for (i = 0; i != read_val; ++i) {
for (int j = 0; j != 6; ++j) {
ret = MDrm_Utils_ReadU32(cur_ptr, size_left, &alloc[i*6+j]);
if (ret) goto EXIT;
cur_ptr += 4; size_left -= 4;
}
}
ret = MDrm_TA_CryptoKeyLadder_RefreshKeys(...);
EXIT:
MDrm_free(alloc);
return ret;
}
根本原因与上一个 bug 完全相同。例如,通过赋予值 0x6666667,分配的大小将为 。read_val
0x80000001 * 0x18 = 0x18
我们通过概念验证触发了此 bug,并获得了以下崩溃:
[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1f00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0xc96000, fault_code: 0x92000047
[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=7182 prefer-ca=7182
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <MDrm_TA_CMD_OEMCrypto_RefreshKeys+0x1b4/0x23c>
[HM] <MDrm_TA_CMD_OEMCrypto_RefreshKeys>+0x18c/0x23c
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM]
[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1f00000022 (tid: 34) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x41414149, fault_code: 0x92000046
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TEE_SERVICE_MUL] 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]
MDrm_TA_OEMCryptoUsageTable_LoadUsageTableHeader
¶可以在函数中找到类似的堆缓冲区溢出:MDrm_TA_OEMCryptoUsageTable_LoadUsageTableHeader
int MDrm_TA_OEMCryptoUsageTable_LoadUsageTableHeader(TEE_Param *param1, uint32_t *a2) {
// [...]
// HMAC/SHA256 verification and AES/CBC/PKCS5 decryption of param1 cur_ptr = plaintext.memref.buffer;
size_left = plaintext.memref.size;
uint64_t *alloc;
// [...]
// 1 call to read_u64_update_ptrs
ret = MDrm_Utils_ReadU32(cur_ptr, size_left, &read_val);
if (ret) goto EXIT;
cur_ptr += 4; size_left -= 4;
ret = MDrm_Memory_Calloc(sizeof(uint64_t) * read_val, alloc);
if (ret) goto EXIT;
for (i = 0; !ret && i != read_val; ++i)
ret = read_u64_update_ptrs(&cur_ptr, &size_left, &alloc[i]);
// [...]
// loading of the usage table header
EXIT:
return ret;
}
根本原因与上一个 bug 完全相同。例如,通过赋予值 0x20000001,分配的大小将为 。read_val
0x20000001 * 8 = 8
不幸的是,我们无法通过概念验证来触发此错误,因为输入数据首先经过身份验证和解密,并且我们没有找到一种独立的方法来泄漏这些操作中使用的密钥(但我们可以使用另一个错误来做到这一点)。
MDrm_TA_CMD_OEMCrypto_CopyBuffer
¶函数中有一个 OOB 写入访问权限,可以通过函数中开始的代码路径访问:MDrm_TA_Decryption_CopyBuffer
MDrm_TA_CMD_OEMCrypto_CopyBuffer
int MDrm_TA_CMD_OEMCrypto_CopyBuffer(int paramTypes, TEE_Param params[4]) {
// [...]
cur_ptr = params[0].memref.buffer;
size_left = params[0].memref.size;
memset_s(&cpybuf, 0x10, 0, 0x10);
// [...] ret = MDrm_Utils_ReadU32(cur_ptr, size_left, &session_id);
// [...]
ret = MDrm_Utils_ReadU32(cur_ptr, size_left, &cpybuf.type);
// [...]
ret = MDrm_TA_ION_MemoryMap(
params[1].memref.buffer, params[1].memref.size, 1, 1, &ionbuf);
// [...]
switch (cpybuf.type) {
// [...]
case 1:
cpybuf.buf = params[2];
ret = MDrm_Utils_ReadU32(cur_ptr, size_left, &cpybuf.offset);
// [...]
break;
// [...]
}
ret = MDrm_Utils_ReadU32(cur_ptr, size_left, &val_read);
// [...]
MDrm_TA_Decryption_CopyBuffer(session_id, &ionbuf, &cpybuf, val_read);
// [...]
if (ionbuf.memref.buffer)
ret = MDrm_TA_ION_MemoryUnMap(
ionbuf.memref.buffer, ionbuf.memref.size, 1, params[1].memref.buffer);
return ret;
}
MDrm_TA_CMD_OEMCrypto_CopyBuffer
从缓冲区中读取两个整数值和 。然后,它将 ION 缓冲区映射到 .如果为 1,则从缓冲区读取另一个整数值 。最后,它读取另一个值并调用 .session_id
cpybuf.type
params[0]
params[1]
ionbuf
cpybuf.type
cpybuf.offset
params[0]
MDrm_TA_Decryption_CopyBuffer
int MDrm_TA_Decryption_CopyBuffer(int session_id, TEE_Param *ionbuf, copy_buf_t *cpybuf, int val_read) {
// [...]
TEE_Param outbuf;
// [...] is_type_2 = 0;
ret = check_copybuf(ionbuf, cpybuf, &outbuf, &is_type_2);
if (!ret && !is_type_2) {
if (MDrm_TA_OEMCryptoEngine_FindSession(session_id, &session)) {
memcpy_s(outbuf.memref.buffer, ionbuf->memref.size,
ionbuf->memref.buffer, ionbuf->memref.size);
} else {
// [...]
}
}
// [...]
return ret;
}
MDrm_TA_Decryption_CopyBuffer
调用。如果检查成功且不是 2,则调用 。如果会话 ID 无效,则它将从 into 复制,大小为 。是输入参数之一,所以让我们看看如何设置 .check_copybuf
cpybuf.type
MDrm_TA_OEMCryptoEngine_FindSession
ionbuf->memref.buffer
outbuf.memref.buffer
ionbuf->memref.size
ionbuf
check_copybuf
outbuf
int check_copybuf(TEE_Param *ionbuf, copy_buf_t *cpybuf, TEE_Param *outbuf, uint8_t *is_type_2) {
// [...]
TEE_Param *out_ionbuf;
// [...] if (cpybuf.type == 1) {
cpybuf_buf = cpybuf->buf.memref.buffer;
cpybuf_size = cpybuf->buf.memref.size;
if (ionbuf->memref.size + cpybuf->offset <= cpybuf_size) {
outbuf->memref.size = ionbuf->memref.size;
is_secure_mem = 0;
ret = MDrm_TA_ION_IsSecureMemory(cpybuf_buf, cpybuf_size, &is_secure_mem);
if (!is_secure_mem)
return 0x77771001;
ret = MDrm_TA_ION_MemoryMap(cpybuf_buf, cpybuf_size, 0, 1, &out_ionbuf);
outbuf->memref.buffer = out_ionbuf->memref.buffer + cpybuf->offset;
return ret;
}
return 0x77770007;
}
// [...]
return ret;
}
check_copybuf
,如果类型为 1,则检查 + <= 。它还确保从安全内存中分配。然后,它调用将其映射到 .最后,设置为 + 和 。ionbuf->memref.size
cpybuf->offset
cpybuf->buf.memref.size
cpybuf
MDrm_TA_ION_MemoryMap
out_ionbuf
outbuf->memref.buffer
out_ionbuf->memref.buffer
cpybuf->offset
outbuf->memref.size
ionbuf->memref.size
问题是加法时可能出现的整数溢出。例如,如果大小为 0x1000,则通过指定偏移量 ,总和将为 。因此,可以指定一个“负”偏移量,该偏移量将通过检查,从而位于 之前。ionbuf->memref.size + cpybuf->offset
-0x1000 = 0xfffff000
0x1000 + 0xfffff000 = 0
outbuf->memref.buffer
out_ionbuf->memref.buffer
回到 ,将 copy from 复制到我们的 out-of-bounds 值中。MDrm_TA_Decryption_CopyBuffer
memcpy_s
ionbuf->memref.buffer
outbuf.memref.buffer
虽然我们为此错误编写了一些概念证明,但它并没有触发崩溃。我们认为这是在内存之前/内存中映射的,并且由于我们只能在范围内指定偏移量,因此 OOB 访问发生在映射的内存上。ionbuf
cpybuf
outbuf
[ -(ionbuf->memref.size) ; cpybuf_size - ionbuf->memref.size ]
二进制漏洞(更新中)
其它课程
windows网络安全防火墙与虚拟网卡(更新完成)
windows文件过滤(更新完成)
USB过滤(更新完成)
游戏安全(更新中)
ios逆向
windbg
恶意软件开发(更新中)
还有很多免费教程(限学员)
更多详细内容添加作者微信