Binder fuzz安全研究
扣吧力作,歡迎轉載,轉載請注明來自colbert337.github.io
最近扣吧忙成狗了,好久沒更新博客,對不住大家了,今天趁天氣暖和點,來一篇干貨。
由于好久沒搞Android了,寫得不專業的地方,請見諒哈。
0.為什么要研究Binder fuzz
以目前最熱門的指紋方案為例。
TZ:Trustzone(請自行百度)
CA:Trustzone框架中的Clinet App
TA:Trustzone框架中的Trustzone APP
上層APP通過Binder機制調用keystore和FingerprintService兩個底層系統服務,來獲取密鑰存儲和指紋的能力。底層服務再通過CA跟TZ驅動通信,調用TZ中TA提供的服務,如指紋識別等安全性要求較高的服務。
我們今天只看Android側的Binder體系。
Binder其實是提供了一種進程間通信(IPC)的功能。這些系統服務,通過binder協議抽象出一個個的“接口”,供其他進程調用,是一個重要的潛在的攻擊面。如果沒有做好權限控制,會讓低權限的第三方應用/病毒/木馬利用,后果不堪設想。
其次,做Android的同學都知道,Binder是android一個非常重要的機制,夸張一點可以說是“Android的靈魂”,非常有必要進行細致的分析和漏洞挖掘。
插播一個扣吧總結的知識點,系統服務的分類
1.Binder體系的java服務(有Stub接口,也就是AIDL封裝)
2.Binder體系的Native服務
3.socket體系的init服務(通常見于init.rc)
4.其他服務
OK,再談談為什么使用fuzz技術呢?
總的來說,是因為fuzz在協議和接口安全測試中比較簡單粗暴,試錯成本低。所以,“不管什么接口,先fuzz一把看看”。
Fuzzing是一種基于缺陷注入的自動軟件測試技術。通過編寫fuzzer工具向目標程序提供某種形式的輸入并觀察其響應來發現問題,這種輸入可以是完全隨機的或精心構造的。Fuzzing測試通常以大小相關的部分、字符串、標志字符串開始或結束的二進制塊等為重點,使用邊界值附近的值對目標進行測試。
主要有兩種類型的fuzzing技術 :
1)dumb fuzzing 這種測試無需了解協議或文件本身格式,通過提供完全隨機的輸入或簡單改變某些字節去發現問題。這種方法實現起來較簡單,容易快速觸發錯誤,但它的完全隨機性會導致產生大量無效的輸入或格式。
2)Intelligent fuzzing 研究目標應用程序的協議或文件格式、功能配置,了解各類漏洞的成因,有目的地編寫fuzzer。編寫有效的fuzzer需要花費時間,但能夠對某些感興趣的部分集中測試,因此更有效。
1.什么是Binder(有基礎的可以略過這一部分)
Android系統采用Binder機制作為進程間通信機制,類似于COM和CORBA分布式組件架構,通俗來講其實就是提供遠程過程調用(RPC)功能。
在Binder機制中,由Client、Server、ServiceManger、Binder驅動這四個部分組成,其中Client、Server、ServiceManager運行在用戶空間,Binder驅動運行在內核空間。Binder就是把這四個組件粘合在一起的粘合劑,核心組件是Binder驅動,ServiceManager提供了輔助管理的功能。Client和Server正是在Binder驅動和ServiceManager提供的基礎設施上,進行CS通信。
下面這個流程圖可以簡單說明Client通過binder調用Server的一個過程,Client會通過Proxy(這里的Proxy不是單一實體,實際上是一系列的BpInterface、BpBinder等代理組件)去跟binder驅動通信,Proxy把數據打包成parcel類型數據再進行傳輸。
那么數據具體是怎么傳輸的呢?
我們繼續深究一下,筆者總結了一個比較全的圖。Java層服務其實也是在Native層服務BpBinder和BBinder的一個封裝。如果屏蔽底層驅動來看,整個Binder代理的核心就是BpBinder和BBinder。
其中,BpBinder最重要的職責就是實現跨進程傳輸的傳輸機制,至于具體傳輸的是什么語義,它并不關心。我們觀察它的transact()函數的參數,可以看到所有的語義都被打包成Parcel類型數據。(Parcel是輕量級的高效的對象序列化和反序列化機制,Android在Java空間和C++都實現了Parcel,由于它在C/C++中,直接使用了內存來讀取數據,因此,它更有效率)
請記住這個偉大的函數——transact()
舉一個例子:上層APP調用MediaRecorder對外提供的API,名字叫setCamera,實際上是執行了BpMediaRecorder中的setCamera方法中,remote()返回的就是BpBinder對象,這里會組裝好parcel數據包,會傳給BpBinder的transact函數。transact函數就會把數據發給對端,也就是另一個BBinder對象。
我們看一下具體是如何發送數據?
BpBinder的transact函數,通過層層調用,最終通過ioctl和binder驅動通信
嗯,上述的就是發送請求的過程。
下面來看接收方,Binder遠程通信的目標端實體必須繼承于BBinder類,該類和BpBinder相對,主要關心的只是傳輸方面的東西,不太關心所傳輸的語義。當收到回復后,會執行IPCTHreadState::waitForRespaonse函數的邏輯,并執行executeCommand(cmd)
executeCommand中,會取得一個合法的BBinder對象,并執行BBinder的transact函數。
(是不是有點奇怪,BBinder也有一個transact函數,請繼續往下看吧)
BBinder::transact中會調用onTransact,這個onTransact才是真正處理業務的。需要注意的是,因為我們的binder實體在本質上都是繼承于BBinder的,而且我們一般都會重載onTransact()函數,所以上面的onTransact()實際上調用的是具體binder實體的onTransact()成員函數。也就是說,onTransact的具體實現一般在上層的binder實體,而不在BBinder。
上面說了,BBinder沒有實現一個默認的onTransact()成員函數,所以在遠程通信時,BBinder::transact()調用的onTransact()其實是Bnxxx或者BnInterface的某個子類的onTransact()成員函數,舉個例子,BnMediaRecorder中實現了一個onTransact函數,通過switch-case,根據不同code進行分發處理。
switch(code)中的code,其實就是前面說的BpBinder中transact函數傳過來的int型的方法號。
2.Binder fuzz怎么作
經過上面的分析,我們已經對Binder有個全局的了解。fuzz的關鍵是選擇好fuzz的目標和fuzz切入點(接口),那么應該如何選擇呢?
思路就是農民斗地主!
前面也說了,系統服務(地主)具有高權限,是我們需要重點關注的對象,而低權限進程(農民)可以利用binder call去調用系統服務,從低權限到高權限,存在一個跨安全域的數據流,這里就是一個典型的攻擊界面。所以,我們選擇系統服務作為fuzz的目標。
那么Fuzz接口呢?選擇fuzz接口需要滿足這幾個要求:
1)這個接口是開放的,是可以被低權限進程調用的
2)這個接口距離fuzz目標(系統服務)比較接近,中間路徑最好透傳,這樣比較容易分析異常
3)從簡原則
根據上面的分析,BpBinder中的transact函數就是一個很好的fuzz接口,但這貨在底層無法直接調用。
怎么辦呢?
我們從BpBinder往上層找,很容易發現,Java層IBinder的transact函數最終調用到BpBinder,且參數是原封不動的“透傳”到底層,考慮到java層的可視化和擴展性,我決定選擇IBinder的公有方法transact作為fuzz接口。
下圖就是這個接口的定義:
請大家認真看看上圖注釋的說明:
code是int類型,指定了服務方法號
data是parcel類型,是發送的數據,滿足binder協議規則,下面會有詳述
reply也是parcel類型,是通信結束后返回的數據
flag是標記位,0為普通RPC,需要等待,調用發起后處于阻塞狀態直到接收到返回,1為one-way RPC,表示“不需要等待回復的”事務,一般為無返回值的單向調用。
下面開始講重點了,額。
接口不是你想fuzz就能fuzz。我們來解決幾個關鍵問題:
1)如何取得服務的IBinder對象?
我們要取到對端的IBinder對象,才可以調用這個服務。系統其實有一些隱藏API可以利用。先通過反射出ServiceManager(hide屬性)中的listServices獲取所有運行的服務名稱:
獲取到String類型的服務名稱后,再反射getService獲取對應的服務IBinder對象:
是不是很犀利,其實是借用了上文說的ServiceManager的強大力量。
2)code如何生成?
code也稱為TransactionID,標定了服務端方法號。
每個服務對外定義的方法都會分配方法號,而且是有規律的,第一個服務方法code使用1,第二個是2,,第三個使用3,依次類推,如果有N個方法,就分別分配1-N個連續的服務號。
有個小技巧,對于Java服務,必定有Stub類,可以通過反射出mInterfaceToken+”$Stub”類中所有成員屬性,其中以”TRANSACTION_”開頭的int型就是該方法對應的。
如下圖的例子,服務端greet方法對應的code就是TRANSACTION_greet:
如果是Native服務,就比較悲劇了,目前還沒有好的自動化方法直接獲取code。一般服務方法數不會太多,所以確定一個上限如50,從1到50循環生成code就可以把所有方法遍歷。當然可以通過人工逆向分析出code,但這樣成本比較高。
3)data如何構造?
通過大量的源碼review和分析得知,data由“RPC header+參數1+參數2+….”來構成的。
舉個例子,如下圖,setDataSource這個API,首先調用data.writeInterfaceToken會寫入一個RPC header,然后會依次寫入調用方法的參數,比如setdataSource有3個參數,這里就會依次寫入三個數據:
是不是很有規律!!
通過review writeInterfaceToken的實現,我們可以發現這個RPC header是由一個int型數據加上String類型的interface name來構成。
但我們不需要自己去構造RPC header,直接調用writeInterfaceToken函數,傳入interface name就可以了。最后抽象出來的parcel類型的data應該是這樣的:
那大家可能會問interface name是什么東西,如何獲取?很簡單,interface name是接口名稱,只要取得IBinder對象,就可以直接getInterfaceDescriptor來獲取interface name,也就是接口方法的描述符。
再看如何獲取一個方法的參數和類型呢?
對于Java層服務的方法,可以通過反射獲取method對象,然后用getParameterTypes獲取所有的類型:
對于native層服務,無法直接獲取方法參數類型,可以用過review調用者實現和反編譯分析等方法來作。
4)fuzz系統和邏輯怎么設計?
直接上圖吧。如下圖,整個fuzz系統分為4個模塊,分別是數據產生器,fuzz引擎,監視器和日志模塊。
1)數據產生器就是用上述方法產生transact需要用到的數據
2)fuzz引擎用于執行具體的transact過程
3)監視器用于監控fuzz結果和異常
4)日志模塊用于記錄fuzz結果
這里筆者采用了3種fuzz方法
1)dumb fuzz:構造好RPC header后,直接塞入大量隨機數據,code范圍為1-100,比較暴力。
2)intelligent fuzz:構造好RPC header后,精準識別出code,并根據不同的code構造出類型正確的隨機參數
3)simple fuzz:構造好RPC header后,精準識別出code,但每次請求只寫入int類型“0”,通過返回值,快速識別fuzz目標的接口是否有權限校驗
5)如何判斷fuzz結果和識別安全漏洞?
一般來說,要做到“權限判斷+數據有效性判斷”兩層防護才是安全的。
通過監控transact的返回值和系統log和系統狀態,可以看到的fuzz現象主要有以下幾種:
1)有SecurityException,則說明該接口有進行權限判斷,做了一層防護
2)無Exception,說明該接口沒有進行權限校驗,默認對外暴露,是不安全的,可以深挖
3)異常現象,如系統重啟、指紋服務掛死、屏幕無響應等,說明該接口不僅沒有進行權限判斷,而且fuzz數據導致了緩存區溢出/進程crash等異常,這類現象要再去進行人工分析,很有可能會嚴重的提權漏洞(比如root)
舉個例子,看看到底是哪里出現安全漏洞。下圖,ontransact函數中switch-case結構里,其中一個case中沒有對數據進行判斷就讀到*device_address,而這個指針直接當成參數直接使用,當指針地址異常就會引起系統服務進程crash,從而導致系統重啟,是一個典型的拒絕服務漏洞。也就是說,任意一個低權限的進程可以隨時進行攻擊,導致系統重啟。
再舉個例子,假設某手機廠家的系統指紋服務有個接口叫DeleteFingerPrint(),用于刪除用戶指紋,該服務的實現沒有進行權限判斷和參數校驗,惡意攻擊者就有可能構造參數,非法調用該服務的方法,把用戶的指紋信息刪除。
重要的事情要說三遍!
參數要做檢查
參數要做檢查
參數要做檢查
今天先寫到這里,寫得有點亂,后續再更新一下。如果你喜歡扣吧的文章,請多多留言支持~
原文地址:http://colbert337.github.io/2016/01/25/binder-fuzz/
總結
以上是生活随笔為你收集整理的Binder fuzz安全研究的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android N混合编译与对热补丁影响
- 下一篇: Android逆向与病毒分析