二进制漏洞分析-13.华为TrustZone TEE_SERVICE_FACE_REC漏洞(一)
2023-11-29 08:0:12 Author: 安全狗的自我修养(查看原文) 阅读量:3 收藏

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

二进制漏洞分析-10.华为TrustZone TEE_SERVICE_VOICE_REC漏洞

二进制漏洞分析-11.华为TrustZone TEE_SERVICE_MULTIDRM漏洞(上)

华为TrustZone TEE_SERVICE_FACE_REC漏洞

此通报包含有关以下漏洞的信息:

  • CVE-2022-48479 漏洞CVE-2022-48479 FI_onExec中未经验证的参数类型

  • CVE-2022-48478 漏洞-2022-48478 HiAiManager::loadModelFromBuffers 中的 OOB 写入

  • CVE-2022-48480 漏洞CVE-2022-48480 FR_TA_CoAuthSignImg中的整数溢出

  • HWPSIRT-2022-47870 FI_onExec中的参数 OOB 访问

  • HWPSIRT-2022-11475 MsgController::_sendMsg 中的空指针取消引用

  • HWPSIRT-2022-62681 Trustlet 函数中的物理地址泄漏 FR_TA_CoAuthSignImg

  • HWPSIRT-2022-97884 Trustlet 函数中的参数指针泄漏 FR_GetHwAuthToken

  • HWPSIRT-2022-28444 Trustlet 函数中的参数指针泄漏 FR_ActiveUserSet

  • HWPSIRT-2022-35074 Trustlet 函数FR_HashCheck中的 ION 虚拟地址泄漏

  • HWPSIRT-2022-75731 Trustlet 函数中的参数指针泄漏 FR_GetResultAuthToken

  • HWPSIRT-2022-84671 Trustlet 函数中的堆指针泄漏 FR_LoadDataBase

  • HWPSIRT-2022-37694 Trustlet 函数中的堆指针泄漏 FR_FaceFeatureAdd

  • HWPSIRT-2022-25260 Trustlet 函数中的参数指针泄漏 FR_SetFidoParam

  • HWPSIRT-2022-80068 Trustlet 函数 FidoWrapUvt 中的堆栈指针泄漏

  • HWPSIRT-2022-65649 Trustlet 函数中的堆指针泄漏 FR_UnwrapFeatureData

  • HWPSIRT-2022-88030 库函数 AlgoManager::createAlgo 中的堆指针泄漏

  • HWPSIRT-2022-84485 库函数 HiAiManager::loadModelFromBuffers 中的 ION 虚拟内存地址泄漏

  • HWPSIRT-2022-14567 库函数 HiAiManager::runModelInMainThread 中的 ION 物理内存泄漏

  • HWPSIRT-2022-59214 库函数 HiAiManager::loadModelFromBuffers 中的 ION 虚拟内存地址泄漏

  • HWPSIRT-2022-77892 库函数 MemoryManager::alloc 中的 ION 虚拟内存地址泄漏

  • HWPSIRT-2022-10579 库函数 MemoryManager::free 中的 ION 虚拟内存地址泄漏

  • HWPSIRT-2022-75307 库函数 MsgController::agentLock 中的指针泄漏

  • HWPSIRT-2022-51942 库函数 CImageBufferAllocator::endAllocatation 中的堆指针泄漏

  • HWPSIRT-2022-95703 库函数 CImageBufferAllocator::endAllocatation 中的堆指针泄漏

  • HWPSIRT-2022-32077 库函数 CImageBufferAllocator::beginAllocatation 中的堆指针泄漏

  • HWPSIRT-2022-41496 库函数 CImageBuffer::d elStride 中的指针泄漏

  • HWPSIRT-2022-32043 库函数 CImageBuffer::fillImage 中的堆指针泄漏

  • HWPSIRT-2022-66227 库函数 CImageBuffer::attachBuffer 中的指针泄漏

  • HWPSIRT-2022-37311 库函数 ImageSourceBase::clear 中的指针泄漏

  • HWPSIRT-2022-75322 库函数 PipelineBuilder::createPipeline 中的堆指针泄漏

  • HWPSIRT-2022-43439 库函数 STFaceidAlgo::loadCpuModel 中的 ION 虚拟地址泄漏

  • HWPSIRT-2022-24846 磁带库功能中的 ION 虚拟地址和堆指针泄漏hw_face_quality_estimation

  • HWPSIRT-2022-13888 库函数中的堆指针泄漏 buffered_free

  • HWPSIRT-2022-75166 磁带库Functionst_tee_initialize中的 ION 虚拟内存地址泄漏

  • HWPSIRT-2022-07617 库函数中的堆指针泄漏 st_tee_detect

  • HWPSIRT-2022-78044 库函数中的堆指针泄漏 st_tee_extract

  • HWPSIRT-2022-97971 库函数st_tee_create_handle中的堆栈指针和 ION 虚拟地址泄漏

  • HWPSIRT-2022-88935 库函数中的堆指针泄漏 gray16to8_hist

  • HWPSIRT-2022-21060 库功能中的虚拟地址泄漏 HIAI_TensorBuffer_createFromTensorDesc

  • HWPSIRT-2022-65800 磁带库功能HIAI_ModelManager_loadFromModelBuffers中的 ION 虚拟内存地址泄漏

  • HWPSIRT-2021-29098 堆栈和堆指针泄漏 FR_AloEnroll

  • HWPSIRT-2021-28201 GetPlainDataWhenEnroll 中的错误检查

整数溢出FR_TA_CoAuthSignImg

函数中存在整数溢出,导致堆缓冲区溢出。FR_TA_CoAuthSignImg

int GetCameraCtrlRemoteData(void **data_buf_p, uint32_t *data_len_p, TEE_Param *params) {
// ...
*data_len_p = params[2].memref.size + 0x18;
*data_buf_p = TEE_Malloc(*data_len_p, 0);
memcpy_s(*data_buf_p, *data_len_p, params[3].memref.buffer, 0x18);
memcpy_s(*data_buf_p + 0x18, *data_len_p - 0x18, params[2].memref.buffer, params[2].memref.size);
// ...
}

在对 的调用中,从堆中分配一个我们命名为 size 的缓冲区,并将 的前 0x18 个字节和所有字节复制到其中。GetCameraCtrlRemoteDatadata_buf0x18 + size of params[2]params[3]params[2]

int FR_TA_CoAuthSignImg(int paramTypes, TEE_Param *params, int nb_params) {
// ...
GetCameraCtrlRemoteData(&data_buf, &data_buf_len, params);
// ...
imagesLen = *((uint32_t *)data_buf + 8);
size = *((uint32_t *)data_buf + 0x12);
extraLen = *((uint32_t *)data_buf + 9);
// ...
if (imagesLen != 1 || extraLen + size + 0x88 != data_buf_len) { /* ... */ }
// ...
if (paramTypes >> 4 == 8) {
phys_addr = params[1].memref.buffer;
phys_size = params[1].memref.size;
} else {
phys_size = params[1].value.b;
_fr_get_static_phy_addr(&phys_addr, 0, params[1].value.a, phys_size);
}
// ...
offset = params[0].value.a;
if (HIDWORD(phys_addr) == 0xFFFFFFFF && phys_addr > ~offset) { /* ... */ }
phys_addr += offset;
// ...
virt_addr = 0;
MapVirtAddr(phys_addr, phys_size, &virt_addr, 0);
memcpy_s(data_buf + 0x22, size, virt_addr, phys_size);
// ...
}

然后从 中提取 3 个 dword 值 , 和 ,并对其值进行某种程度的验证。但是,在第二次检查中,添加 和 时可能存在整数溢出。因此,可以通过仔细选择 的值来使具有任何值。imagesLensizeextraLendata_bufextraLen + size + 0x88 != data_buf_lenextraLensizesizeextraLen

接下来,从参数(直接指定或使用 ION 缓冲区)获取物理地址,并向其添加任意偏移量。此物理地址范围映射到 TA 中。最后,将其内容复制到堆分配的缓冲区中。由于源地址和大小由用户控制,并且目标大小也可以控制,因此这会导致完全受控的堆缓冲区溢出。

在我们的概念验证代码中,我们使对象溢出了 8 个字节,导致在释放对象时检测到损坏的页脚。尽管如此,我们也可以触发更大的溢出,并利用经典的堆利用技术,如堆取消链接,来获得写入原语。

[HM] ERROR: free: corrupted footer

未经验证的参数类型FI_onExec

当到达 中的所有命令处理程序时,TEE 参数类型未得到验证。虽然命令处理程序似乎期望 中的输出值 、 中的输入缓冲区和 中的输出缓冲区,但这实际上从未在任何地方强制执行(尤其是在 和函数中)。FI_onExecparams[0]params[1]params[2]TA_InvokeCommandEntryPointFI_onExec

TEE_Result TA_InvokeCommandEntryPoint(
void *sessionContext,
uint32_t commandID,
uint32_t paramTypes,
TEE_Param params[4]) {
// ...
if ((commandID & 0x80000) != 0) {
algo_api = GetAlgoApi();
// ...
params[0].value.b = algo_api->FI_onExec(
commandID,
params[1].memref.buffer,
params[1].memref.size,
params[2].memref.buffer,
params[2].memref.size);
// ...
}
// ...
}
int FI_onExec(
unsigned int commandID,
void *buffer1,
unsigned int length1,
void *buffer2,
unsigned int length2,
uint32_t paramTypes) {
// ...
switch (commandID) {
case 0x80001:
FaceIdCore::set_log_level(g_faceid_core, *buffer1);
return 0;
case 0x80002:
image_info.field_0 = 0;
FaceIdCore::get_version(g_faceid_core, &image_info.field_0);
*(uint32_t *)buffer2 = image_info.field_0;
return 0;
}
// ...
}

因此,通过传递需要缓冲区的值,可以很容易地获得任意内存读写。

例如,可以传递一个值并调用命令 0x80001,从而将任意地址的值设置为日志级别。这是使用第一个概念验证代码来演示的,该代码导致在地址0xdeadbeef读取无效的内存:params[1]

[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: 0x92000005
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TEE_SERVICE_FAC] 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=7827 prefer-ca=7827
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <_Z9FI_onExecjPvjS_j+0x8c/0x9e4>
[HM] <TA_InvokeCommandEntryPoint>+0x4a0/0x8e8
[HM] <TA_InvokeCommandEntryPoint>+0x4a0/0x8e8
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM]

例如,还可以传递 can 调用命令 0x80002 的值,从而导致版本被写入任意地址。这是使用第二个概念验证代码来演示的,该代码导致地址0xdeadbeef处的内存写入无效:params[2]

[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_SERVICE_FAC] 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=7895 prefer-ca=7895
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <_Z9FI_onExecjPvjS_j+0xcc/0x9e4>
[HM] <_Z9FI_onExecjPvjS_j>+0xc4/0x9e4
[HM] <TA_InvokeCommandEntryPoint>+0x4a0/0x8e8
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM]

OOB 写入HiAiManager::loadModelFromBuffers

中有一个 OOB 写入,允许将用户控制器大小的用户控制数据写入用户控制器地址。可以使用两个命令触发此漏洞,这些命令将在下一节中详细介绍。HiAiManager::loadModelFromBuffers

动态初始化

要执行的第一个命令是 。DynamicInit

它的处理程序函数调用 ,执行以下操作:HandleDynamicInitCommandFR_TA_DynamicInit

  • 它调用 ,从输入缓冲区中提取(除其他外)和;FaceIonCheckisFinishconfigSizeparams[3]

  • 它调用 ,将参数 ION 缓冲区的物理地址和大小复制到 (和FaceIonGetg_ion_buffersg_ion_count);

  • 它调用 ,将物理地址范围映射到虚拟地址空间;FaceIonMap

  • 它使用 ION 缓冲区和配置(在输入缓冲区中)进行调用。FI_dynamicInitparams[3]

int HandleDynamicInitCommand(uint32_t paramTypes, TEE_Param params[4]) {
// ...
FR_TA_DynamicInit(params, paramTypes);
}
int FR_TA_DynamicInit(TEE_Param *params, int paramTypes) {
// ...
FaceIonCheck(params[3].memref.buffer, params[3].memref.size, &isFinish, &configSize, g_ion_count);
// ...
FaceIonGet(params, paramTypes, g_ion_buffers, &g_ion_count);
// ...
if (!isFinish)
return 0;
// ...
FaceIonMap(g_ion_buffers, g_ion_count, 0, &ion_count);
// ...
g_algo_api->FI_dynamicInit(
g_ion_buffers,
g_ion_count,
params[3].memref.buffer + 0x10,
configSize);
// ...
}

该函数只需调用 ,即可执行以下操作(除其他外):FI_dynamicInitFaceIdCore::init

  • 它将配置保存到类成员中;

  • 它将 的第一个 ION 缓冲区细分为缓冲区数组:ionBufferssubbuffers

    • 子缓冲区的数量来自(用户控制);config.field_4C

    • 每个子缓冲区的大小来自(也由用户控制);config.field_24

  • 它使用subbuffers;

  • 它使用第二个缓冲区初始化 NpuRunMem;

  • 它使用第三个缓冲区初始化 NpuLoadModelMem;

  • 它使用第四个缓冲区初始化 CpuModelMem。

int FI_dynamicInit(FIIonBuf *ionBuffers, int ionCount, void *config, uint configSize) {
// ...
FaceIdCore::init(g_faceid_core, ionBuffers, ionCount, config, configSize);
}
int FaceIdCore::init(FaceIdCore *this, FIIonBuf *ionBuffers, int ionCount, void *config, uint configSize) {
// ...
memcpy(&this->config, config, sizeof(this->config));
// ...
memset(subbuffers, 0, sizeof(subbuffers));
offset = 0;
for (int i = 0; i < this->config.field_4C; i++) {
subbuffers[i].vaddr = ionBuffers[0].vaddr + offset;
subbuffers[i].paddr = ionBuffers[0].paddr + offset;
subbuffers[i].size = this->config.field_24[i];
offset += subbuffers[i].size;
}
// ...
MemoryManager::initMemory(&this->memory_manager, &subbuffers[0], 1);
// ...
HiAiManager = HiAiManager::getHiAiManager();
HiAiManager::setSysFunc(HiAiManager, &this->sysfuncs, &this->memory_manager, this->config.field_14);
HiAiManager::setNpuRunMem(HiAiManager, &subbuffers[1]);
HiAiManager::setNpuLoadModelMem(HiAiManager, &subbuffers[2]);
HiAiManager::setCpuModelMem(HiAiManager, &subbuffers[3]);
// ...
}

由于子缓冲区的大小在添加之前从未检查过,因此通过为缓冲区指定虚假大小,可以使下一个缓冲区在第一个 ION 缓冲区的选定偏移处具有 /。ii+1paddrvaddrionBuffers

因此,可以使用具有位于内存中任何位置的虚拟地址的缓冲区来调用(如果我们知道原始缓冲区的虚拟地址)。此函数将字段设置为(除其他外)我们选择的值。HiAiManager::setNpuLoadModelMemthis->npuLoadMem

int HiAiManager::setNpuLoadModelMem(HiAiManager *this, FIIonBuf *buffer) {
// ...
this->npuLoadMem.vaddr = buffer->vaddr;
this->npuLoadMem.paddr = buffer->paddr;
this->npuLoadMem.size = buffer->size;
return result;
}

荷载模型

要执行的第一个命令是 。LoadModel

它的处理程序函数调用 。它执行与映射 ION 缓冲区大致相同的操作,并仅使用 ION 缓冲区调用函数。HandleLoadModelCommandFR_TA_LoadModelFR_TA_DynamicInitFI_loadModel

int HandleLoadModelCommand(uint32_t paramTypes, TEE_Param params[4]) {
// ...
FR_TA_LoadModel(params, paramTypes);
}
int FR_TA_LoadModel(TEE_Param params[4], uint32_t paramTypes) {
// ...
FaceIonCheck(params[3].memref.buffer, params[3].memref.size, &isFinish, 0, g_ion_count2);
// ...
FaceIonGet(params, paramTypes, g_ion_buffers2, &g_ion_count2);
// ...
if (!isFinish)
return 0;
// ...
FaceIonMap(g_ion_buffers2, g_ion_count2, 1, &ion_count);
// ...
g_algo_api->FI_loadModel(g_ion_buffers2, g_ion_count2);
// ...
}

该函数仅调用 ,该函数使用消息类型 0x10 进行调用,并将 ION 缓冲区作为参数。此消息由函数中的另一个线程处理。FI_loadModelFaceIdCore::load_modelMsgController::sendMsgHiAiManager::loadModelFromBuffers

int FI_loadModel(FIIonBuf *ionBuffers, int ionCount) {
// ...
return FaceIdCore::load_model(g_faceid_core, ionBuffers, ionCount);
}
int FaceIdCore::load_model(FaceIdCore *this, FIIonBuf *ionBuffers, int ionCount) {
// ...
msg_controller = MsgController::getMsgController();
msgArg[0] = ionBuffers;
msgArg[1] = ionCount;
msgArg[2] = 0;
MsgController::sendMsg(msg_controller, 0x10, msgArg, 0xC);
}

除其他事项外,该函数将 ION 缓冲区(包含模型)复制到 中定义的内存区域中。由于我们之前已设置为用户控制的值,因此调用的所有参数都在我们的控制之下。HiAiManager::loadModelFromBuffersthis->npuLoadMemthis->npuLoadMemmemcpy_s

int HiAiManager::loadModelFromBuffers(HiAiManager *this, FIIonBuf *ionBuffers, int ionCount) {
// ...
vaddr = this->npuLoadMem.vaddr;
sizeOfBufs = 0;
for (int i = 0; i < ionCount; i++) {
memcpy_s(vaddr, ionBuffer[i].size, ionBuffer[i].vaddr, ionBuffer[i].size);
vaddr += ionBuffer[i].size;
sizeOfBufs += ionBuffer[i].size;
}
// ...
}

物理地址泄漏

最后一个缺失的部分是知道映射第一个 ION 缓冲区的虚拟地址。这非常简单,因为此虚拟地址是使用 in (called from ) 写入日志的。tee_printMemoryManager::initMemoryFaceIdCore::init

int MemoryManager::initMemory(MemoryManager *this, FIIonBuf *buffers, int count) {
// ...
for (int index = 0; index < count; index++) {
this->field_8[index] = buffers[index].vaddr;
this->field_30[index] = buffers[index].size;
++this->count;
if (FILog::mLogLevel > 2)
tee_print(
0,
"%s %d:[%s][%s]%s index:%d, size:%d , addr:%p\n",
"[info]",
0x21,
&FILogTag,
"MemoryManager",
"initMemory",
index,
buffers[index].size,
buffers[index].vaddr);
}
// ...
}

在我们的概念验证代码中,我们设置为 0xdeadbeef,从而在调用时触发此地址的崩溃:this->npuLoadMem.vaddrmemcpy_s

[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1e00000036 (tid: 54) 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_SERVICE_FAC] tid=54 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=6970 prefer-ca=6970
[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]

二进制漏洞(更新中)

其它课程

windows网络安全防火墙与虚拟网卡(更新完成)

windows文件过滤(更新完成)

USB过滤(更新完成)

游戏安全(更新中)

ios逆向

windbg

恶意软件开发(更新中)

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

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


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