Huawei TrustZone VprTa漏洞
二进制漏洞分析-5.华为安全监控漏洞(SMC MNTN OOB 访问)
如果你是想谈业务合作,直接翻到底联系作者。
不闲聊,直接说重点,我能不能做,你有没有预算,相关先介绍(我介绍技术、或者方案产品,你介绍需求带预算)就完事。
云桌面开发相关: 虚拟化(usb、usb透传、显示器、网卡、磁盘、声卡、摄像头)模块。
安全产品(dlp/edr/沙箱)开发相关:文件单/双缓冲透明加解密、网络防火防墙、所有通用外设管控(用户层版/驱动层版)模块、其它管理(恶意进程、模块等)。
通用开发相关:注入、hook、产品方案编写与设计、非黑灰产逆向、具体单独小功能编写。
如果你想系统学习二进制漏洞技能,往最后翻,或者直接翻到底联系作者。
此通报包含有关以下漏洞的信息:
CVE-2021-39997 漏洞CVE-2021-39997 IsGmmModelLoaded OOB 访问
CVE-2021-39997 漏洞CVE-2021-39997 InitGetScoreParams OOB 访问
CVE-2021-39997 漏洞CVE-2021-39997 GmmGetScore OOB 访问
HWPSIRT-2022-85498 LTopProb 中的 OOB 访问
HWPSIRT-2022-62034 XvectorLoadModels 中的参数缓冲区溢出
HWPSIRT-2022-44993 InitGetScoreParams 中的参数缓冲区过度读取
我们从以下方面发现了 3 个影响 VprTa 可信应用程序的漏洞:/vendor/bin/
中,模型类型未选中,导致 OOB 访问 和GmmLoadModels
IsGmmModelLoaded
GetGmmInfo
;
在 中,从输入缓冲区获取的值未选中,导致整数溢出期间,并因此导致堆缓冲区溢出;InitGetScoreParams
TEE_Malloc
in ,则 的返回值未选中,从而导致 OOB 访问。GmmGetScore
GetGmmInfo
我们发现另外 3 个漏洞会影响 Huawei 的 VprTa 可信应用程序。HwVAssistant_OVE.apk
VprTa TA 符合 GlobalPlatform TEE 内部核心 API。因此,它从位于正常环境中的客户端应用程序接收命令。这些命令由函数处理。TA_InvokeCommandEntryPoint
IsGmmModelLoaded
OOB 访问¶的参数未得到验证,特别是作为 和 的参数给出的参数。如果指定大于 1,则对数组的访问将超出范围。返回的指针可以引用位于数组之后以及数组之前的内存,方法是利用 上的整数溢出。GmmLoadModels
modelType
IsGmmModelLoaded
GetGmmInfo
modelType
g_gmmModel
GetGmmInfo
g_gmmModel
0x18 * modelType
bool IsGmmModelLoaded(int modelType) {
return g_gmmModel[0x18 * modelType]
&& *(uint32_t *)&g_gmmModel[0x18 * modelType + 0x14] != 0;
}void *GetGmmInfo(int modelType) {
if (g_gmmModel[0x18 * modelType])
return &g_gmmModel[0x18 * modelType + 4];
return 0;
}
int GmmLoadModels(const char *path, int modelType, int model, int modelSize) {
// [...]
if (IsGmmModelLoaded(modelType)) {
GmmInfo = GetGmmInfo(modelType);
if (!memcpy_s(model, modelSize, GmmInfo + 0x10, 0x21008))
return 0;
}
return Load(path, strlen(path), model, modelSize);
}
如果 的地址已知,攻击者可以计算出正确的偏移量,从而计算出返回指向用户控制缓冲区的指针所需的值。由于此指针随后被取消引用,并且该值在调用中用作源,并且由于目标是另一个用户提供的输出缓冲区,因此可用于读取任意内存位置。g_gmmModel
modelType
GetGmmInfo
memcpy_s
我们无法找到这样的信息泄漏,因此我们编写的概念证明仅触发 OOB 内存访问:
[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: 0xe51954cc, fault_code: 0x92000005
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[VprTa] 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=6856 prefer-ca=6856
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <IsGmmModelLoaded+0x10/0x44>
[HM] <GmmLoadModels>+0x30/0x100
[HM] invalid fp. backtrace abort
[HM] Dump task states END
[HM]
[HM] [TRACE][1212]pid=43 exit_status=130
InitGetScoreParams
OOB 访问¶的参数是用户提供的输入缓冲区。In 包含整数值,特别是:world
InitGetScoreParams
偏移量 4 处的帧数;
偏移处的要素数0x70814。
unsigned int InitGetScoreParams(uint32_t *world, world_info_t *info) {
// [...]
info->frames = world[1];
info->features = (uint32_t *)TEE_Malloc(4 * world[1], 0);
// [...]
for (int frame_idx = 0; frame_idx < world[1]; ++frame_idx) {
info->features[frame_idx] = (uint32_t *)TEE_Malloc(4 * world[0x1C205], 0);
// [...]
for (int feat_idx = 0; feat_idx < world[0x1C205]; ++feat_idx) {
info->features[frame_idx][feat_idx] = world[frame_idx * 0x20 + feat_idx + 0x19002];
}
}
return 0;
}
在调用函数中检查帧数,并且必须小于 400。未选中要素的数量。通过指定大量功能,将溢出,并且在第二次调用中分配的缓冲区将小于实际需要的缓冲区。因此,写入访问将超出堆分配缓冲区的范围。GmmGetScore
4 * world[0x1C205]
TEE_Malloc
info->features[frame_idx][feat_idx]
在我们的测试中,缓冲区似乎分配的地址小于缓冲区,允许攻击者控制写入的目标地址。但一个问题仍然存在:如何在到达堆的末尾之前阻止溢出?从理论上讲,应该可以通过指定减少偏移量来使每次写入覆盖指针,从而导致在同一位置写入。但我们没有走这条路,我们的概念证明只是在用户控制的地址上触发崩溃:info->features[frame_idx]
info->features
info->features[frame_idx]
[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: 0xfa7000, fault_code: 0x92000047
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[VprTa] 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=6480 prefer-ca=6480
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <InitGetScoreParams+0x100/0x150>
[HM] <memset_s>+0x28/0x38
[HM] <GmmGetScore>+0xf0/0x240
[HM] <TA_ProcGmmGetScore>+0x5c/0xd4
[HM] <TA_InvokeCommandEntryPoint>+0xb0/0x180
[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=[VprTa] 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]
GmmGetScore
OOB 访问¶In 中,已正确验证,但使用 的返回值时未检查模型是否已加载。GmmGetScore
modelType
GetGmmInfo
void *GetGmmInfo(int modelType) {
if (g_gmmModel[0x18 * modelType])
return &g_gmmModel[0x18 * modelType + 4];
return 0;
}unsigned int GmmGetScore(int path, int pathSize, unsigned int modelType, int model, double *ret_p) {
// [...]
// modelType validation
// [...]
GmmInfo = GetGmmInfo(modelType);
if (GmmInfo->field_10) {
// use GmmInfo
// [...]
}
return 0;
}
如果未加载模型,则返回 NULL,并且地址0x10的读取访问发生在 :GetGmmInfo
GmmInfo->field_10
[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: 0x10, fault_code: 0x92000006
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[VprTa] 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=7343 prefer-ca=7343
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <GmmGetScore+0x6c/0x240>
[HM] <GmmGetScore>+0x64/0x240
[HM] <TA_ProcGmmGetScore>+0x5c/0xd4
[HM] <TA_InvokeCommandEntryPoint>+0xb0/0x180
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM]
[HM] [TRACE][1212]pid=46 exit_status=130
LTopProb
¶命令 (ID #1) 中调用的函数中有一个 OOB 访问。LMixProb
LTopProb
TA_ProcGmmGetScore
uint32_t TA_ProcGmmGetScore(TEE_Param params[4], uint32_t *result_p) {
/* [...] */
ret = GmmGetScore(
params[0].memref.buffer,
params[0].memref.size,
params[1].value.a,
params[2].memref.buffer,
&score)
/* [...] */
}
在 中,函数调用负责用来自第三个输入缓冲区的信息填充结构。GmmGetScore
TopDistribs
info
TEE_Param
uint32_t GmmGetScore(void *ibuf0_addr, uint32_t ibuf0_size, uint32_t ival1_a,
void *ibuf2_addr, uint32_t score_p)
{
/* [...] */
info = TEE_Malloc(0x14, 0);
/* [...] */
model = GetGmmInfo(ival1_a);
/* [...] */
// Copies the offset from the TEE_Param input buffer into the toplist of
// `info`
ret = TopDistribs(info, ibuf2_addr, 0x1E);
/* [...] */
// Copies the toplist of `info` into `model`
ret = CopyTopDistribs(model, info);
/* [...] */
value = LTopProb(model, info->features);
/* [...] */
}
TopDistribs
将值从数组中加载到数组中,如下所示:ibuf2_addr
topList
uint32_t TopDistribs(model_t *info, uint32_t *ibuf2_addr, uint32_t topDistribNB) {
/* [...] */
info->topDistribNB = topDistribNB;
/* [...] */
info->topList = TEE_Malloc(4 * info->frames, 0);
/* [...] */
for (i = 0; i < info->frames; ++i) {
info->topList[i] = TEE_Malloc(4 * info->topDistribNB, 0);
/* [...] */
for (j = 0; j < info->topDistribNB; ++j)
info->topList[i][j] = ibuf2_addr[2 + j];
/* [...] */
}
}
topList
然后从函数中的 into 复制:info
model
CopyTopDistribs
uint32_t CopyTopDistribs(model_t *model, model_t *info) {
/* [...] */
model->topDistribNB = info->topDistribNB;
/* [...] */
model->topList = TEE_Malloc(4 * info->frames, 0);
/* [...] */
for (i = 0; i < model->frames; ++i) {
model->topList[i] = TEE_Malloc(4 * model->topDistribNB, 0);
/* [...] */
for (j = 0; j < model->topDistribNB; ++j)
model->topList[i][j] = info->topList[i][j];
/* [...] */
}
}
然后,调用函数,然后使用用户控制的元素进行调用:GmmGetScore
LTopProb
LMixProb
topList
uint32_t LTopProb(model_t *model, uint32_t **features) {
/* [...] */
value = LMixProb(model, *features, model->topList[frame][i]);
/* [...] */
}
LMixProb
最后使用 ,可以是任意 32 位整数,在取消引用之前计算 的地址。这会导致 OOB 访问(相对于 )。top
buf_addr
model->buffer
uint32_t LMixProb(model_t *model, uint32_t *features, uint32_t top) {
/* [...] */
buffer = model->buffer;
buf_addr = &model->buffer[0x40 * top + 0x400];
/* [...] */
value = *(uint64_t *)(buf_addr + 8);
/* [...] */
}
我们通过概念证明触发了此 bug,并在未映射地址的读取访问时获得了以下崩溃:
[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: 0xb1675f18, fault_code: 0x92000005
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[VprTa] 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=7323 prefer-ca=7323
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <LMixProb+0xa0/0x120>
[HM] <LTopProb>+0x80/0xfc
[HM] <LTopProb>+0x80/0xfc
[HM] <GmmGetScore>+0x1dc/0x284
[HM] <TA_ProcGmmGetScore>+0x38/0xac
[HM] <TA_InvokeCommandEntryPoint>+0x118/0x1c8
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM]
XvectorLoadModels
¶从命令 (ID #7) 调用的函数中存在参数缓冲区溢出。XvectorLoadModels
TA_ProcLoadXvectorModel
int TA_ProcLoadXvectorModel(TEE_Param params[4], uint32_t *result_p) {
// ...
vectorLoadModels(params[0].memref.buffer, params[1].memref.buffer);
// ...
}
在 中,加载模型后,它们将被复制到参数输出缓冲区中。从不检查参数输出缓冲区的大小,并且最多可以复制 0x33 * 0x464 = 0xdfec 个字节。如果缓冲区小于预期,这将导致缓冲区溢出。XvectorLoadModels
unsigned int XvectorLoadModels(void *inbuf, void *outbuf) {
// ...
for (int i = 0; i != 0x33; ++i) {
outbuf[i * 0x464 + 0x460] = XvectorInfo[i].size;
memcpy_s(&outbuf[i * 0x464], 0x460, XvectorInfo[i].data, 0x460);
}
}
我们通过概念验证触发了此 bug,并在位于 param 输出缓冲区之后的地址进行写入访问时获得了以下崩溃:
[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: 0x7000631c, fault_code: 0x92000047
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[VprTa] 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=7829 prefer-ca=7829
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <XvectorLoadModels+0x80/0xd0>
[HM] <memcpy_s>+0x60/0x6c
[HM] <TA_ProcLoadXvectorModel>+0x14/0x2c
[HM] <TA_InvokeCommandEntryPoint>+0x16c/0x1c8
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM]
InitGetScoreParams
¶从命令调用的参数缓冲区过度读取 (ID #1)。InitGetScoreParams
TA_ProcGmmGetScore
int TA_ProcGmmGetScore(TEE_Param params[4], uint32_t *result_p) {
// ...
GmmGetScore(
params[0].memref.buffer,
params[0].memref.size,
params[1].value.a,
params[2].memref.buffer,
&score);
// ...
}
int GmmGetScore(void *ibuf0_addr, int ibuf0_size, int ival1_a, void *ibuf2_addr, int score_p) {
// ...
InitGetScoreParams(ibuf2_addr, info);
// ...
}
在此函数中,从参数输入缓冲区中提取许多值,包括偏移量为 0x70814 的特征数。但是,在输入此函数之前,从不检查参数输入缓冲区的实际大小,因此可能小于 0x70818 字节,从而导致缓冲区覆盖。
int InitGetScoreParams(uint32_t *ibuf2_addr, model_t *info) {
// ...
featureNB = ibuf2_addr[0x1C205];
// ...
}
我们通过概念验证触发了此错误,并在位于参数输入缓冲区之后的地址的读取访问上获得了以下崩溃:
[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: 0x70074814, fault_code: 0x92000007
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[VprTa] 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=7971 prefer-ca=7971
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <InitGetScoreParams+0x74/0x10c>
[HM] <memset_s>+0x28/0x38
[HM] <GmmGetScore>+0x128/0x284
[HM] <TA_ProcGmmGetScore>+0x38/0xac
[HM] <TA_InvokeCommandEntryPoint>+0x118/0x1c8
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM]
我们验证了这些漏洞是否影响了以下设备:
麒麟990:P40 专业版 (ELS)
请注意,其他型号可能已受到影响。
名字 | 严厉 | CVE漏洞 | 补丁 |
---|---|---|---|
IsGmmModelLoaded OOB 访问 | 危急 | CVE-2021-39997 漏洞CVE-2021-39997 | 2022 年 2 月 |
InitGetScoreParams OOB 访问 | 危急 | CVE-2021-39997 漏洞CVE-2021-39997 | 2022 年 2 月 |
GmmGetScore OOB 访问 | 危急 | CVE-2021-39997 漏洞CVE-2021-39997 | 2022 年 2 月 |
OOB 访问LTopProb | 重复 | 不适用(*) | 固定 |
参数缓冲区溢出XvectorLoadModels | 重复 | 不适用(*) | 固定 |
参数缓冲区过度读取InitGetScoreParams | 重复 | 不适用(*) | 固定 |
(*)华为关于APP漏洞的中、高、严重声明:
它们通过AppGallery升级来解决。通常,不会为此类漏洞分配 CVE 编号。
2021年10月12日,华为PSIRT收到第一份漏洞报告。
2021年10月28日 - 华为PSIRT确认发布首份漏洞报告。
2022年2月1日 - 华为PSIRT表示,这些问题已在2022年2月的更新中修复。
2022年2月1日,华为PSIRT收到第二份漏洞报告。
2022年4月01日 - 华为PSIRT确认了第二批漏洞报告,并表示在修复第一批漏洞时,内部也发现了第二批漏洞。
从 2022 年 11 月 30 日至 2023 年 7 月 19 日 - 我们定期交换有关公告发布的信息。
二进制漏洞(更新中)
其它课程
windows网络安全防火墙与虚拟网卡(更新完成)
windows文件过滤(更新完成)
USB过滤(更新完成)
游戏安全(更新中)
ios逆向
windbg
恶意软件开发(更新中)
还有很多免费教程(限学员)
更多详细内容添加作者微信