作者:王鐵磊
原文鏈接:https://mp.weixin.qq.com/s/Lj8c5PLzLGIfdBoDzairsQ
1 引言
虛擬內存 (Virtual Memory, VM) ?系統是現代操作系統基礎核?組件,不僅負責虛擬地址和物理內存的映射關系,管理調度物理內存的使?,為程序開發提供統?透明的地址空間,同時也要為不同執?環境提供隔離,管控物理頁?讀、寫、執?等權限,是系統安全的基?。由于VM?系統需要同時兼顧性能、效率、透明性和安全等?標,導致VM?系統在實現過程中邏輯?多異常復雜,VM?系統中的各種優化策略也就成了"邏輯錯誤"類型漏洞的重災區。
本?以iOS、macOS操作系統的內核XNU為例,回顧?些與XNU VM?系統相關的歷史漏洞;通過分析這些漏洞的成因,梳理VM?系統邏輯漏洞的脈絡,希望能給其他安全研究帶來?些啟發。
2 未預期的破壞
VM?系統的?個經典功能是Swap,是指在調度物理頁?時,VM系統可能會將部分物理頁?轉儲?磁盤從?獲得?夠的物理空間;當這些轉儲的物理頁?被真正訪問時,VM?系統再從Swap?件中恢復原始物理頁?內容。
2017年,Google Project 0研究員Ian Beer與Jann Horn在頭腦風暴中,想到?個問題,這個Swap?件能否被篡改破壞?與其百思不解,不如簡單?試。macOS系統上,Swap?件路徑是/private/var/vm/swapfifile0。Ian Beer簡單粗暴的?隨機數據覆蓋了該?件:

結果也?較粗暴,內核直接崩潰了[1]。這意味著macOS上在SIP[2] 保護機制并沒有保護這個Swap?件。在處理被破壞的Swap?件時,內核出現了內存錯誤。?膽思考,勇于嘗試,是亙古不變的道理。
3 未預期的共享
共享內存 (Shared Memory) 是操作系統中實現進程間通信的重要?式,通過把相同的物理頁?映射在不同執?體的虛擬地址空間,使雙?都能訪問同樣的物理頁?,不僅能減少物理頁?的使?,也能避免通信過程中傳輸?塊數據,從?提?通信的效率。不過,如果多?對同?塊內存都具有寫權限時,維護內存?致性變得很困難,"競爭寫"也容易引發很多安全問題。
共享內存的雙取 (Double Fetch) 是?類?常典型的安全漏洞成因。下表展?了?個簡單的雙取漏洞:第?個?調?strlen計算共享內存中?個字符串的長度;第??根據該長度分配?個本地堆內存;第三?調用strcpy把共享內存中的字符串復制到新分配的本地內存中。這三?代碼的問題在于,因為C string以\0為截?符,strlen掃描字符串時以第?次遇到的\0計算當前字符串的長度,同樣, strcpy 復制字符串時,直到遇到的\0才會終?復制。?共享內存另?端控制者,可以在strlen和strcpy之間,把第?個\0修改為?零字符,這導致strcpy會復制過多字符到 local_buffer 中,造成堆溢出。

隨著系統復雜性的增?、系統通信層級越來越多,底層開發者與應?開發者針對數據傳輸和使?的視?很難統?,導致很多情況下數據是以?預期的共享內存形式傳遞的,造成很多安全問題。接下來,我們來看?個?預期共享的案例。
3.1 CVE-2017-7047:xpc_data共享內存傳輸
XNU提供了基于Mach Port和Mach Message的靈活通信機制。在MachMessage基礎上,?戶態進?步封裝了libxpc框架,提供了字典、隊列、字符串、純數據等常見數據結構的封裝;在libxpc基礎上,又封裝了NSXPC框架,重點?持遠程對象和?法調?。
2017年,Ian Beer發現,libxpc在傳輸xpc_data時,如果數據長度超過0x4000,會調? mach_make_memory_entry_64創建虛擬內存的mach port,然后將mach port發送出去;接收?收到這個mach port后,調?mach_vm_map將port對應的虛擬內存再映射到本地。具體流程如下圖所?。
為避免共享內存的隱患,發送?調? mach_make_memory_entry_64時,使?了 MAP_MEM_VM_COPY標志。結合XNU中的注釋,不難理解使?這個標志位創建mach port過程中,會創建數據的副本。這樣接收?通過mach_vm_map再次映射后,獲得的也是數據副本。這其實是?種將xpc_data以寫時復制(Copy-on-Write, COW)形式傳遞的實現?式,避免了xpc_data的完全共享。

然?,Ian Beer敏銳地發現,這種COW依賴于發送?創建mach port時指定MAP_MEM_VM_COPY標志。對于"惡意"發送?,完全可以創建?個全共享內存的port,然后發送給接收?。這樣通過mach_vm_map簡單映射獲得的虛擬地址,會和發送?完全共享物理頁?。這樣?來,接收?使?xpc_data時就可能存在雙取問題。
Ian Beer繼續追蹤系統中對xpc_data的不安全使?。NSXPC是在libxpc基礎上,在進程間通信中?持遠程對象和遠程?法調?。在實現中,這些遠程對象和?法調?經序列化后由xpc_data發送。Ian Beer在這個反序列化過程中,把?個雙取問題轉換成了堆溢出,實現了針對任意NSXPC服務的原型攻擊 [4]。
Apple的漏洞修復?案也很清晰。在傳輸xpc_data過程中,不再信任發送?,?是在接收?調?mach_vm_map時,強制開啟copy選項,也就是以COW形式映射。這樣發送?對 xpc_data 的任何修改都不會傳遞到接收?,避免了雙取問題。
3.2 IOKit Out-of-line數據
IOKit是XNU的驅動開發框架,提供了?戶態程序、內核、設備之間的通信接口。其中,?戶態程序可以通過 IOConnectCallMethod 接口與內核驅動傳遞數據。當?戶態傳??塊數據時(Out-of-line, OOL),系統會創建 IOMemoryDescriptor,將該段數據映射到內核供驅動使?。然?XNU-3789.31.2版本之前,IOKit開發者沒有意識到,這段數據實際是以共享內存形式存在的。IOKit框架和具體驅動開發者之間并沒有清晰界定OOL數據的存在形態,以?于很多驅動實現中都有雙取漏洞。更多漏洞細節可以參考Flanker的blog [5]。
例如,在macOS顯卡驅動中, IOAccelDisplayPipePostCSCGammaVID::init 函數在處理OOL輸?時,會根據OOL內的?個整數調? IOMalloc 分配內存,然后再次讀取該整數?于 memcpy 。這種典型的雙取漏洞造成極容易利?的堆溢出[6]。
鑒于太多驅動開發者都沒有意識到OOL數據是通過共享內存傳遞的,逐?糾正驅動開發者的代價太?,Apple在XNU-3789.31.2中,直接將OOL數據以COW形式映射。相應的補丁如下。通過使? kIOMemoryMapCopyOnWrite 標志,確保內核獲得的數據副本不會存在雙取問題。
3.3 Apple Neural Engine共享內存問題
IOKit處理OOL時犯過的錯誤,也會反應在單獨的驅動中。除了直接使?OOL數據,IOKit驅動也可以??映射?戶態內存?內核使?。2018年,Apple推出了A12仿?芯?,搭載了強?的神經?絡引擎。相應地,iOS內核中也增加?個H11ANEIn驅動,?于處理神經?絡引擎的相關計算請求。H11ANEIn需要?量異步處理,IOKit框架提供的OOL數據并不適合其計算需求,因此H11ANEIn直接根據?戶態提供的地址創建了 IOMemoryDescriptor 。
不幸的是,H11ANEIn開發者顯然不清楚IOKit的歷史舊賬,在創建IOMemoryDescriptor時,僅使?了 kIODirectionOutIn參數,也就是“讀寫”權限。H11ANEIn在使?這段數據時更加肆意,直接把?個Port指針保存在這段內存。因為這段內存被內核和?戶態共享,?戶態不僅可以直接獲取這個Port指針造成內核地址空間的信息泄漏,也能直接任意替換這個Port指針,通過偽造Port指針獲取內核控制權[7]。這個漏洞?iOS 12版本引?,直到iOS 13.6才被修復;上?IOKit框架處理OOL數據的問題隱藏的更久,這些也印證了?預期共享問題的隱蔽性。
4 復雜的COW
對于?預期共享類型的問題,?個直接的修復?案就是以寫時復制(Copy-on-Write, COW)分享數據。COW是VM?系統的?個經典優化策略,其核?思想是同?個物理頁?可以同時映射在不同進程的虛擬地址空間內,任意??試圖修改物理頁?內容時,系統會為其分配?個原物理頁?的副本頁?,這樣寫操作最終作?在副本頁?,?不會影響原始頁?,從?這個寫操作也不會被另??所感知。COW原理簡單?實現復雜。很多操作系統在COW的實現上出現過問題,例如2016年Linux系統中的臟? (Dirty COW) 漏洞。下?我們看?個XNU中COW相關的安全問題。
4.1 既共享又COW (CVE-2017-2456)
COW通常把?個物理頁?以read-only權限映射到兩個虛擬地址,然后任意?個虛擬地址發?寫操作的時候,系統會捕獲頁?寫異常,在異常處理過程中復制新的物理頁?并更新映射關系。如果虛擬地址VA和虛擬地址VB是COW關系,?虛擬地址VA和虛擬地址VC是完全共享關系,即同?個物理頁?被映射到三個(甚?更多)虛擬地址時,系統如何處理通過虛擬地址VC發?的寫操作呢?這并不是?個容易回答的問題。
帶著這個疑慮,Lokihardt做了?個測試 [2]。他創建了?個Memory entryport后,通過完全共享的形式把這個內存頁?映射在兩個不同的虛擬地址VA和VC。然后將VA通過復雜消息 Mach Message發送到另?個進程。根據MachMessage的傳遞規則,消息接收?會以COW的形式映射VA對應的物理內存?虛擬地址VB。但是,Lokihardt發現此時在發送?修改VC內容,并不會觸發系統的COW語意;換??之,通過VC的所有寫,在VB端全部可見。Lokihardt基于這個思路,在libxpc反序列過程中發現了內存雙問題,利?雙取引發的內存溢出,實現了對任意libxpc服務的攻擊 (CVE-2017-2456)。在修復這個漏洞時,XNU嚴格檢查了物理頁?是否多重映射,確保COW的?致性。
4.2 隱蔽的寫操作
COW實現的?個關鍵點在于:捕獲寫操作。這個問題似乎很簡單,將物理地址以只讀權限映射,寫操作?然就會觸發異常。但是如果寫操作并不是通過虛擬地址來實現,COW就可能出現問題。
iOS設備上配備了專門的協處理器?持快速圖像縮放、?彩轉換等操作。內核中通過?個名為AppleM2Scaler的驅動協調?戶態和協處理器的通信。對于圖像縮放,本質上是?戶態指定?個?標內存區域和?個源內存區域,AppleM2Scaler通知協處理器通過DMA?式直接從源內存區域讀取數據處理后寫??標區域。然?,AppleM2Scaler忽略了?戶態內存的讀寫屬性。這導致?戶態應?可以通過AppleM2Scaler驅動修改任意只讀內存。
這個漏洞?Linux上的臟?漏洞還要嚴重。2018年,陳良利?該漏洞 [8],在應?程序內存空間內修改了?塊只讀內存;這塊只讀內存本來僅內核可寫,內核在使?這些數據時不再進?驗證;陳良利?AppleM2Scaler篡改這段只讀內存后觸發內核其他漏洞,實現iOS的越獄。
這個漏洞還有很多其他利??式。iOS設備上動態鏈接庫都被提前鏈接保存在?個shared cache?件中。這個shared cache在設備啟動之初,被加載映射到內存中。隨后所有啟動的進程,都會共享這個shared cache內存。當然對于其中的代碼頁?,應?程序僅具有讀+執?的權限。AppleM2Scaler這個漏洞可以直接篡改shared cache代碼頁?,造成在?權限進程中的任意代碼執?。值得?提的是,iOS設備上開啟了強制代碼簽名機制。修改代碼頁?后,必須避免系統對頁?再次進?簽名驗證。這需要通過其他?些技巧阻?被修改的頁?觸發page fault。
除了DMA,系統還可能有其他“隱蔽寫”操作。Jann Horn針對?件映射內存做了?些研究 [9,10],發現了?些攻擊路徑。例如,把?件映射到內存后,以COW形式分享給另?個進程,此時?件內容緩存在物理內存頁?。當系統內存吃緊時,?件內存頁?會被交換出去;但是Jann Horn發現,當這些內存頁?再次被訪問時,系統會從磁盤中重新讀取?件恢復頁?內容。這就造成了?個攻擊窗口。如果?件來源于攻擊者??加載的?件系統鏡像,攻擊者可以直接修改(pwrite) 這個?件系統鏡像從?修改相應?件內容。這樣從?件中再次恢復物理頁?內容時,物理頁?內容不再與之前頁?內容?致,破壞COW的語意。
4.3 危險的鎖
2018年,Ian Beer 發現XNU在處理COW映射時,有這樣?個優化策略:當?個進程通過mach message把?個虛擬地址VA對應的內存以COW形式發送出去,并且在mach message中指明消息發送后就在本地釋放虛擬地址VA時,XNU會忽略COW?直接把 VA對應的內存項移到接收?,省掉了將內存變為COW所需的頁?權限修改的過程。
然?,這個優化策略實現的過程中存在條件競爭 [11],導致?個進程可以同時把VA發送給另?個進程和??。這樣另外?個進程和??進程都作為這個VA的接收?,都會獲取這個VA對應內存的訪問權限;?根據優化策略,這兩次接受都不會激活COW復制。Ian Beer利?這個特性,在A12機型上重現了上?Lokihardt針對libxpc反序列化的雙取漏洞攻擊。
Ian Beer在2018年12?報告了這個問題,Apple在2019年初對該問題做了?次修復。差不多時隔?年后,2019年10?Ian Beer再次分析這個漏洞時,發現由于條件競爭的復雜性,Apple的這次修復并不完整。Ian Beer再次提交了PoC。根據Ian Beer 的報告,Apple在2020年初再次對漏洞修復。
5 結語
本?回顧了XNU在VM管理層?的?些歷史漏洞,尤其是圍繞COW實現的各個環節,分析了各種的漏洞成因。盡管這些漏洞已經修復,其實還有很多開放性問題是本?沒有解答的。例如,Apple針對這些漏洞的修復是否完備?有沒有其他途徑繞過這些修復?隨著系統功能的不斷變化,會不會再次引?未預期的共享?除了?件映射內存和DMA,系統中是否還存在隱蔽的寫操作繞過COW?現有的COW實現是不是還有漏洞?希望這些問題能引發?家的思考,激發?家靈感去尋找新的安全問題。
6 參考文獻
-
MacOS uses an insecure swap file. https://bugs.chromium.org/p/project-zero/issues/detail?id=1131, 2017.
-
About System Integrity Protection on your Mac. https://support.apple.com/en-us/HT204899
-
macOS/IOS: mach_msg doesn't copy memory in a certain case. https://bugs.chromium.org/p/project-zero/issues/detail?d=1083. 2017.
-
Many iOS/MacOS sandbox escapes due to unexpected shared memory-backed xpc_data object. https://bugs.chromium.org/p/project-zero/issues/detail?id=1247. 2017.
-
Racing for everyone: descriptor describes TOCTOU in Apple’s core. https://blog.flanker017.me/racing-for-everyone-escriptor-describestoctou/
-
Pwning the macOS Sierra Kernel Inside the Safari Sandbox. https://github.com/wangtielei/Slides/blob/main/Shakacon_2017.pdf
-
Don’t place a port in shared memory. https://blog.pangu.io/?p=221 .
-
KeenLab iOS Jailbreak Internals. https://i.blackhat.com/us-18/Wed-August-8/us-18-Chen-KeenLab-iOS-Jailbreak-Internals.pdf
-
XNU: copy-on-write behavior bypass via partial-page truncation of file. <https:// bugs.chromium.org/p/project-zero/issues/detail?id=1725. 2018.
-
XNU: copy-on-write behavior bypass via mount of user-owned filesystem image. https://bugs.chromium.org/p/project-zero/issues/detail?id=1726.2018.
-
XNU vm_map_copy optimization which requires atomicity isn't atomic. https:// bugs.chromium.org/p/project-zero/issues/detail?id=1728. 2018.
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1875/
暫無評論