二进制漏洞分析-8.Huawei TrustZone VprTa漏洞
2023-11-22 07:39:10 Author: 安全狗的自我修养(查看原文) 阅读量:7 收藏

Huawei TrustZone VprTa漏洞

二进制漏洞分析-5.华为安全监控漏洞(SMC MNTN OOB 访问)

如果你是想谈业务合作,直接翻到底联系作者。

  1. 不闲聊,直接说重点,我能不能做,你有没有预算,相关先介绍(我介绍技术、或者方案产品,你介绍需求带预算)就完事。

  2. 云桌面开发相关: 虚拟化(usb、usb透传、显示器、网卡、磁盘、声卡、摄像头)模块。

  3. 安全产品(dlp/edr/沙箱)开发相关:文件单/双缓冲透明加解密、网络防火防墙、所有通用外设管控(用户层版/驱动层版)模块、其它管理(恶意进程、模块等)。

  4. 通用开发相关:注入、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 访问 和GmmLoadModelsIsGmmModelLoadedGetGmmInfo;

  • 在 中,从输入缓冲区获取的值未选中,导致整数溢出期间,并因此导致堆缓冲区溢出;InitGetScoreParamsTEE_Malloc

  • in ,则 的返回值未选中,从而导致 OOB 访问。GmmGetScoreGetGmmInfo

我们发现另外 3 个漏洞会影响 Huawei 的 VprTa 可信应用程序。HwVAssistant_OVE.apk

VprTa TA 符合 GlobalPlatform TEE 内部核心 API。因此,它从位于正常环境中的客户端应用程序接收命令。这些命令由函数处理。TA_InvokeCommandEntryPoint

IsGmmModelLoadedOOB 访问

的参数未得到验证,特别是作为 和 的参数给出的参数。如果指定大于 1,则对数组的访问将超出范围。返回的指针可以引用位于数组之后以及数组之前的内存,方法是利用 上的整数溢出。GmmLoadModelsmodelTypeIsGmmModelLoadedGetGmmInfomodelTypeg_gmmModelGetGmmInfog_gmmModel0x18 * 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_gmmModelmodelTypeGetGmmInfomemcpy_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

InitGetScoreParamsOOB 访问

的参数是用户提供的输入缓冲区。In 包含整数值,特别是:worldInitGetScoreParams

  • 偏移量 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。未选中要素的数量。通过指定大量功能,将溢出,并且在第二次调用中分配的缓冲区将小于实际需要的缓冲区。因此,写入访问将超出堆分配缓冲区的范围。GmmGetScore4 * world[0x1C205]TEE_Mallocinfo->features[frame_idx][feat_idx]

在我们的测试中,缓冲区似乎分配的地址小于缓冲区,允许攻击者控制写入的目标地址。但一个问题仍然存在:如何在到达堆的末尾之前阻止溢出?从理论上讲,应该可以通过指定减少偏移量来使每次写入覆盖指针,从而导致在同一位置写入。但我们没有走这条路,我们的概念证明只是在用户控制的地址上触发崩溃:info->features[frame_idx]info->featuresinfo->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]

GmmGetScoreOOB 访问

In 中,已正确验证,但使用 的返回值时未检查模型是否已加载。GmmGetScoremodelTypeGetGmmInfo

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的读取访问发生在 :GetGmmInfoGmmInfo->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

OOB 访问LTopProb

命令 (ID #1) 中调用的函数中有一个 OOB 访问。LMixProbLTopProbTA_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)
/* [...] */
}

在 中,函数调用负责用来自第三个输入缓冲区的信息填充结构。GmmGetScoreTopDistribsinfoTEE_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_addrtopList

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 复制:infomodelCopyTopDistribs

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];
/* [...] */
}
}

然后,调用函数,然后使用用户控制的元素进行调用:GmmGetScoreLTopProbLMixProbtopList

uint32_t LTopProb(model_t *model, uint32_t **features) {
/* [...] */
value = LMixProb(model, *features, model->topList[frame][i]);
/* [...] */
}

LMixProb最后使用 ,可以是任意 32 位整数,在取消引用之前计算 的地址。这会导致 OOB 访问(相对于 )。topbuf_addrmodel->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) 调用的函数中存在参数缓冲区溢出。XvectorLoadModelsTA_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)。InitGetScoreParamsTA_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漏洞补丁
IsGmmModelLoadedOOB 访问危急CVE-2021-39997 漏洞CVE-2021-399972022 年 2 月
InitGetScoreParamsOOB 访问危急CVE-2021-39997 漏洞CVE-2021-399972022 年 2 月
GmmGetScoreOOB 访问危急CVE-2021-39997 漏洞CVE-2021-399972022 年 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

恶意软件开发(更新中)

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

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


文章来源: http://mp.weixin.qq.com/s?__biz=MzkwOTE5MDY5NA==&mid=2247489986&idx=1&sn=d51c20eefb1a061a98e201b3223cc484&chksm=c13f2a8bf648a39d26d9881148aaa83bfea8793343f28a39c7b86b26a64a1701d5078c47a2d8&scene=0&xtrack=1#rd
如有侵权请联系:admin#unsafe.sh