當前位置:
首頁 > 最新 > Mac OS 內核擴展漏洞挖掘指導流程

Mac OS 內核擴展漏洞挖掘指導流程

一次性進群,長期免費索取教程,沒有付費教程。

教程列表見微信公眾號底部菜單

雜談與概述

首先表達一下我對漏洞挖掘的一些理解,這些理解可以讓大家了解目前我是怎麼看漏洞挖掘的,進而了解為什麼會形成文章中提到的流程與方法。

漏洞挖掘是沒有辦法保證結果的:沒有人敢說隨便給他一個程序,他就可以從中找到漏洞。也有人說漏洞挖掘靠運氣,我理解的運氣是指:漏洞是由開發人員生產的,漏洞挖掘人員只是去發現開發人員生產的漏洞,對漏洞挖掘人員來說,審計的模塊中是否存在漏洞是未知的。

既沒法保證結果,又存在運氣成分,難道漏洞挖掘是玄學不成?我認為不是。我們沒法保證結果,但是我們可以保證過程。何為保證過程?保證過程是指我們可以在領域內總結出一套流程,總結出一些需要關注的、常規的審計點。流程與審計點代表的是我們團隊的經驗與當前水平,如果嚴格執行了流程與審計點後,還是沒法在目標中找到漏洞,那說明以我們團隊目前的水平就是無法在目標中找到漏洞。如何對待運氣?我認為有兩個方法,一是憑藉審計人員的經驗,以及對系統功能的整體了解,他可以「感覺到」系統的薄弱點在哪裡,然後去尋找漏洞。另一個是強調覆蓋面,我們既然無法確定哪個模塊存在漏洞,那我們就一個模塊一個模塊的去做覆蓋。

人工審計與模糊測試的關係,這也是無法迴避的問題。其實人工審計與模糊測試之間的關係就是如何利用資源的問題。人工審計花費的是人力資源,而模糊測試花費的機器資源。有些問題適合用人力資源來解決,另一些問題適合使用機器來解決,二者沒有任何矛盾,因此這不是一個二選一的問題,而是一個如何充分利用資源的問題。現在大家都有一個共識:人力成本要比電費貴,最終我們還是希望用機器來解決大部分問題。但是對於一個具體的團隊來說,囿於團隊的知識背景、知識儲備、對問題理解的深度、對問題抽象的高度,無法對現有的所有漏洞進行建模,也就無法實現自動化的漏洞挖掘。雖然如此,我們的長遠目標仍然是最大限度的實現自動化,提高生產效率,好像歷屆工業革命都不是靠砸人完成的。

這篇文章介紹了從拿到一個內核擴展的二進位文件開始,到完成漏洞挖掘的整個過程:每一步需要做什麼,每一步需要做到什麼程度,以及在漏洞挖掘的過程中我們需要審計的點。最重要的是,大家要明確我們的 Target:尋找漏洞,凡是與尋找漏洞無關的事情我們都不感興趣,比如:逆向。但是逆向又是我們必須要做的,因此還會提供一系列的工具,來自動化部分逆向工作,盡量縮短從二進位文件到漏洞挖掘的路徑。

從文章的結構上看,首先我們會回顧一些常見的攻擊面,然後會介紹如何確定目標,並提供一些思路與工具幫助大家確定目標。在確定了目標後,會介紹如何對二進位文件做逆向,對於二進位審計來說這是非常重要的一個階段。在完成逆向工作後,我們進入漏洞挖掘階段,這也是發揮大家聰明才智的階段,在這裡會介紹一些常見的漏洞類型與審計方法。

常規攻擊面

這裡會介紹內核與內核擴展的攻擊面,關於通用的內核攻擊面並不是什麼秘密,大家都知道,就是如下幾個:

BSD 系統調用。

Mach 系統調用。

MIG 介面。

設備的創建及 ioctl。創建設備的函數包括:cdevsw_add, cdevsw_add_with_bdev, bdevsw_add,大家只要在內核中或者內核擴展中查找相關的函數即可,找到相關結構後,重點關注 ioctl 的響應函數。

網路協議。網路協議是通過調用 net_add_proto 添加的。需要關註:setsockopt、ioctl 的處理函數,以及對網路包的解析。

sysctl 變數。

驅動暴露的介面。驅動主要會向用戶空間暴露這幾個功能:讀取、設置屬性;創建共享內存;通知對象的管理;externalMethod,getTargetAndMethodForIndex, 這部分介面。

上面列出的是粗粒度的攻擊面,對於更具體的、更細粒度的攻擊面,大家需要深入進去,比如:以 externalMethod 為例,大家需要深入分析每個實現。

確定目標

方法一:由方法主導。就是找到一種新的攻擊方法,使原來不是攻擊面的目標變成攻擊目標。這個只能多多進行思維的發散,沒人可以教這個能力。

方法二:通過大量的源碼閱讀與逆向分析,對系統非常熟悉,可以跨越方法、跨越類、跨越模塊思考問題、尋找問題。軟體設計的一種原則就高內聚、低耦合,低耦合不是沒有耦合,存在耦合即存在互相影響,安全影響也是其中的一種影響。尋找這類安全問題,思維要在低層次與高層次之前隨意切換,前提就是對功能的了解。沒有什麼捷徑,多讀源碼、多逆向、多調試,如果可以做到比開發人員還了解相關的功能,自然能發現別人發現不了的問題。

方法三:一個一個的覆蓋。沒有太多技巧,參考前面的各種列表,進行地毯式覆蓋。雖然沒有技巧,但是閉源的程序、歷史久遠的程序中存在的漏洞數量會相對多些。閉源的程序主要指驅動,大家可以使用工具與內核類列表獲得用戶空間中可以打開的驅動列表,隨便選一個進行逆向、審計。

逆向過程

逆向逆的是什麼?通過逆向,我們主要想從程序中獲得兩方面的信息:邏輯;數據結構、類型系統。IDA Pro 的反編譯插件極大的降低了分析程序邏輯的難度。這裡我們會圍繞如何還原內核擴展中的數據結構來講解逆向過程,通過良好的數據結構,配合 F5 插件,可以極大的提交逆向出的程序的可讀性。

插播廣告,關於逆向的程度不知道大家是如何理解的,我對逆向的入門是通過這本書《Reversing:逆向工程揭密》。這麼多年過去了,仍沒忘記書中的觀點,大致是:我們逆向出來的源碼,通過使用合適的編譯選項,可以生成逆向目標相同的二進位。這個目標太高了,我們在實際中確實沒必要做到,但是想提醒一點:當你對目標做了審計,沒有發現任何漏洞後,你應該反問自己對目標做了什麼程度的逆向與調試?!

Note:下文會涉及到一些例子,例子使用的二進位是:macOS-10.12.4-16E195, AppleCameraInterface.kext, 5.59.0。

在 IDA Pro 中完成對目標驅動的自動分析後,我們還需要做如下幾件事:

創建虛函數表、類結構。

導入依賴的類型。

增加 Got 表的可讀性。

識別導入的虛函數表。

識別 sMethods。

識別 externalMethods 的參數。

獲取類的布局信息。

識別 sysctl 結構。


為了提高 F5 偽代碼的可讀性,我們需要為 C++ 的虛函數表、類創建相應的結構體,大家可以使用工具來實現這個目標。在完成轉換後,在 Structures 標籤頁下,大家可以看到如下的信息:

虛函數表會被轉換成如下的結構:

如上圖,「`vtable for』AppleCamIn」主要用於依賴導入,「`vtable for』AppleCamIn1」主要用來構造對象的 vptr,如下圖:

大家會注意到如上的 2 個虛函數表中有大量的重複成員,我們目前並沒有做去重,大家可以使用結構體嵌結構體的方式來去重,進而節省 IDA Pro Database 的存儲空間與載入速度。另,大家可以使用工具所提供的功能來實現:在反編譯窗口中,雙擊虛函數後,直接跳轉到具體的實現。

為了輔助分析,大家使用 File -> Product File -> Create C Header File 來查看創建的結構及成員變數的類型:


打開驅動的 Info.plist 文件,查看 OSBundleLibraries:

可以看到當前的驅動依賴 Kernel 和 IOPCIFamily。使用 IDA 載入 Kernel,使用腳本創建相應的虛表結構與類結構,然後導出類型 IDC:File -> Product File -> Dump Typeinfo to IDC File。最後在當前的 IDA 數據中導入 IDC,IDC 是 IDA 的原生腳本,導入的方式是:File -> Script file…,選擇導出的 IDC 文件。需要將同樣的操作流程應用到依賴庫上,本例中是 IOPCIFamily。有的依賴庫並不會在 OSBundleLibraries 中列出來,以 AppleCameraInterface 為例,它還依賴 IOACPIFamily 和 IOSurface,像這種隱含的依賴,遇到時再做處理。

在完成依賴庫的導入後,我們現在需要處理 Got 表,下圖是處理之前的 Got 表:

以 IOService 的虛表為例,命名 off_12038 沒有意義,造成在逆向分析時還要多一步跳轉才能知道這是什麼類型:

使用工具處理 Got 表,處理後的效果如下:

在閱讀彙編時可以直接明白這個符號的意義。同時這個腳本還可以處理一部分虛表的偏移,轉換成函數,如下圖:


處理完 Got 表後,我們對上面提到的函數進行反編譯,結果如下圖:

可以看到具體的函數還是使用偏移值 +0x4B,原因是導入的虛函數表是在運行時處理的,靜態解析不出來。大家可以使用工具做處理,原理是:單獨創建一個 Segment 用來存在虛表,然後修改相關的引用,這樣反編譯插件就可以找到相應的函數。處理後的反編譯效果如下圖:


sMethods 是驅動的一個非常重要的攻擊面,因此找到並識別出相關的結構是重要的一步。大家可以使用工具來識別、處理相關結構。

識別前:

識別後:

大家還可以使用工具來為結構添加索引,如下圖:

大家可以使用工具來處理,處理前:

處理前的反編譯結果:

處理後的效果:

處理後的反編譯效果:

該工具沒有做 CFG 跟蹤,無法處理所有情況,剩下的情況需要大家手工處理,手工處理的方式為,選中相關的指令行:

然後使用快捷鍵:T,調出菜單為寄存器指定類型:

確認無誤後,點擊 OK。這種方法也可以用來處理虛表、類等結構。


在逆向部分的開頭我們已經說明了,逆向主要逆的就是數據結構。這裡會介紹一種逆向數據結構的可復用的流程與方法,這種逆向方法使用腳本來表達逆向的結果,具體的逆向結果。

內核擴展中的類對成員變數的設置主要在 init 與 start 兩個函數中完成,下面我們以 AppleCamIn::start 為例來說明對類結構的還原。

處理的腳本如下圖:

start 函數的初始反編譯效果如下圖:

將 v6 轉成 IOPCIDevice *,方法是右鍵 v6,在彈出的菜單中選擇:Convert to struct *:

轉換後的效果圖如下:

可以看到虛表已經被識別出來的。通過 this->gap[31] = (__int64)v6; 知道,有一個成員變數是 IOPCIDevice,我們在腳本中為類添加一個成員變數:

其中成員的偏移量的計算方法為:gap 的類型是 int64,gap 中每個成員的大小為 8 位元組,成員相對於 gap 的偏移量為 31 * 8,因此總的偏移量為:8 + 31 * 8。也可以在反彙編窗口看直接的偏移量,然後在腳本中使用直接偏移量:

然後執行腳本,再次按 F5,刷新反編譯結果,可以看到:

繼續識別成員變數,IOPCIDevice::mapDeviceMemoryWithRegister 返回的類型是 IOMemoryMap *,我們將 v9 轉換成對應的類型:

從反編譯結果中我們我們知道 AppCamIn 類有個成員變數是 IOMemoryMap *,gap_0x8 的類型是 int64,起始偏移量為 this[15],相對偏移量為 9 * 0x8,總的偏移量為:this[15] + 9 * 0x8 = 0x8 * 15 + 9 * 0x8,將成員變數添加到類結構中:

再次執行腳本,F5 刷新反編譯結果:

以此類推,可以完成類成員的識別與結構化。

使用工具來查找、識別 sysctl 結構。


一句話:不要急躁,如果找不到漏洞,那就什麼都不用想,按照如上的流程與方法,安安靜靜的做好逆向。

常見漏洞與審計

基本的錯誤類型大家可以通過閱讀推薦資料(《The Art of Software Security Assessment: Identifying and Preventing Software Vulnerabilities》,ISBN: 978-0321444424)來熟悉、掌握。對於 Mac 平台特有的錯誤類型,大家可以通過閱讀 Google Project Zero 的漏洞報告來學習。這裡多說一句,Google Project Zero 對行業的貢獻不只只是提高了 IT 基礎設施的安全性,同時也為我們提供了關於漏洞的、大量的、真實的漏洞與利用資料,大家一定要充分利用。

另外,就像這個世界上每天都會發生地震一樣,漏洞領域每天都會有漏洞被公開,面對每天大量的信息我們該如何應對?這裡給大家的建議是:

1、只關注當前領域的信息,對於其他的信息如果時間不夠,可以略過。什麼是當前領域?我們當前做二進位漏洞,二進位漏洞就是我們當前的領域,具體包括:漏洞披露、利用方法與思路、漏洞挖掘生產力工具。

2、對於具體的二進位漏洞,我們沒有時間去復現每一個漏洞,實現每一個利用,大家可以鍛煉下自己的抽象、總結能力:通讀漏洞報告後,用一句話總結出錯誤類型或利用技巧方面的創新。

言歸正傳,完成逆向處理後,大家可以相對容易的找到如下幾個常見的安全問題:

memcpy, bcopy 等溢出問題。

整數的溢出、符號問題。

競態條件問題。

二進位協議,即:從用戶空間向內核中傳遞一個結構體。

直接的信息泄露,即:從內核向用戶空間拷貝數據,大小可控。

之所以說容易是指:這些錯誤涉及的功能比較小,我們通過在反編譯結果中跟蹤用戶空間可控的數據在內核空間的傳遞,就可以相對直觀的發現。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 計算機與網路安全 的精彩文章:

Steam 新型盜號木馬及產業鏈分析報告
APT團伙新利用漏洞樣本分析及關聯挖掘

TAG:計算機與網路安全 |