华为TrustZone TEE_Weaver漏洞
此通报包含有关以下漏洞的信息:
- CVE-2021-40022 漏洞 InterfaceRead 中缺少输入参数检查
华为TEE_Weaver TA符合GlobalPlatform TEE Internal Core API。因此,它从位于正常环境中的客户端应用程序接收命令。这些命令由函数处理。TA_InvokeCommandEntryPoint
此 TA 实现了 5 个命令:、、和 。GetConfig
InterfaceWrite
InterfaceRead
GetStatus
CollectRetryErr
信息泄露¶
有 3 次对 in 的调用,这会导致 中的信息泄露。这些基元可用于泄漏堆栈地址、缓冲区地址,从而泄漏受信任应用程序的 ASLR 幻灯片。SLog
InterfaceWrite
InterfaceRead
logcat
第一个调用在函数中: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
第二个调用在函数中,在我们的例子中,该函数已内联在函数中:CheckReadResp
DoRead
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_OUTPUT
TEE_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.buffer
memref.size
value.a
value.b
value.a
InterfaceRead
ReturnReadResult
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否则会提前返回。b
memcpy_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 的白名单中。teecd
teecd
由于我们不想费心在这些二进制文件之一中注入代码,因此我们选择通过修补内核驱动程序来规避身份验证,以添加模拟任何原生二进制文件/APK 的功能。
泄漏 ASLR 幻灯片¶
为了泄露 trustlet 的基址,我们在命令中使用了日志字符串。我们可以通过发送写入命令来触发此代码路径,以在未使用的插槽(此处为插槽 #63)中设置 weaver 令牌的值,然后发送同一插槽的读取命令以将其读回。CheckReadResp
剩下要做的就是解析命令的输出。我们执行它,过滤掉不是来自 的消息,然后查找字符串。logcat
teeos-TEE_WEAVER
response 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_check
cmd_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.so
libtee_shared_a32.so
libvendor_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 日 - 我们定期交换有关公告发布的信息。
原文始发于微信公众号(安全狗的自我修养):二进制漏洞分析-9.华为TrustZone TEE_Weaver漏洞
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论