二进制漏洞分析-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
,
&
params
[
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
不幸的是,我们无法通过概念验证来触发此错误,因为输入数据首先经过身份验证和解密,并且我们没有找到一种独立的方法来泄漏这些操作中使用的密钥(但我们可以使用另一个错误来做到这一点)。
OOB 写入访问权限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 ]
原文始发于微信公众号(安全狗的自我修养):二进制漏洞分析-11.华为TrustZone TEE_SERVICE_MULTIDRM漏洞(上)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论