macOS 二進制 plist 解析的漏洞分析
屬性表文件(Plist)是一種文件形式,通常用于儲存用戶設置,也可以用于存儲捆綁的信息,該功能在舊式的Mac OS中是由資源分支提供的。由于Plist中存儲的數據是抽象的,其采用的文件格式可以不止一種。
macOS看起來非常有趣,尤其是屬性列表(plists)是存儲序列化對象的文件,在蘋果操作系統中很常見,類似于微軟Windows使用注冊表存儲配置數據。下面顯示了macOS應用程序自動器的基于xml的屬性列表示例。此屬性列表存儲應用程序的版本信息以及其他有用數據如下所示:

屬性列表也可以采用二進制形式,否則稱為bplist。顧名思義,這些是二進制格式的屬性列表,這些屬性列表啟用了一些附加的對象類型和關系,包括字典。以下是一個示例bplist,盡管可以使用其他標簽來支持該格式的其他版本,但是可以通過bplist00標記來標識該示例:

通過查看蘋果的開源代碼,可以更好地理解bplist格式,我也發現這個參考非常有用。蘋果開源代碼中定義的bplist格式如下:
二進制plist格式
屬性列表為模糊測試提供了一個有趣的目標,因為它們很容易被操作系統的許多部分(包括更高特權的進程)創建和使用。蘋果公司的開源代碼使我能夠創建任意的bplist,并開始模糊文件格式,同時使用內置的macOS plutil命令行工具確保正確的語法。
我花了幾天時間來生成bplist來執行該格式,并很快發現某些對象類型在被常見的macOS二進制文件(例如Finder)以及更高特權的二進制文件(包括Launch Services守護程序(LSD))進行解析時會導致異常。系統崩潰日志表明Core Foundation框架中存在漏洞,但是,正如我們稍后將看到的,此漏洞存在于多個位置。Core Foundation(簡稱CF)是一套Mac OS和iOS中的C語言API,由較低層的一些例程和封裝函數組成。Apple發布的最大的一個CF開源項目叫CFLite,基于CFLite可以開發跨Mac OS X,Linux和Windows平臺的應用。另外一個第三方的開源實現叫做OpenCFLite也有同樣的功能。大多數的Core Foundation例程的對象通常遵循這樣的命名規則,例如:CFDictionary開頭的函數中會出現CFDictionaryRef,這些對象經常被會手動通過CFRetain和CFRelease來管理引用計數。在內部,Core Foundation也會把一些基礎類型轉成Objective-C運行時中可用的格式。
大多數生成的崩潰似乎是由Core Foundation解析bplist并隨后嘗試使用創建的對象的方式引起的。任何bplist的ObjectTable中有非字符串類型的對象(Date、Data、Bool等)都會在調用不存在的字符串相關選擇器時導致解析過程崩潰。其結果是,任何使用Core Foundation讀取屬性列表的進程都可能因無法識別的選擇器異常而崩潰。下面是一個示例易受攻擊的代碼路徑:

使用以下C代碼以及與測試應用程序位于同一目錄中的名為Info.plist的惡意屬性列表,可以輕松到達此位置:
崩潰分析
可以通過編程方式或通過將修改后的bplist放置在系統上自動對其進行分析的方式來為此漏洞生成崩潰,實際上,此漏洞的最初跡象之一是LSD在嘗試使用修改后的屬性列表注冊應用程序時反復在我的系統上崩潰。
Objective-See有一篇很棒的博客文章,詳細介紹了通過LSD進行的應用程序注冊以及屬性列表的自動解析。
下圖是控制臺輸出,顯示了在我的桌面上以合法的Info.plist身份修改后的bplist導致崩潰發生的頻率,還請注意用戶級和系統級進程崩潰。
LSD崩潰
通過修改單個字節以將ASCII字符串對象(類型0x5X)更改為另一種對象類型,如DATE(類型0x33),可以創建惡意的bplist。修改后的bplist示例如下:

這個很小的字節更改現在可以用于在macOS系統和iOS上造成嚴重破壞,盡管在此研究中未對該平臺進行過測試。這種方法還會影響包括Spotlight在內的多個數據庫,這些數據庫已被該惡意Info.plist攻擊,甚至在重新啟動后也反復導致崩潰。
漏洞是如何發現的?
由于能夠輕松地重新創建崩潰,因此我深入研究了該漏洞的實際發生位置。跟蹤此漏洞的一種簡單方法是查看崩潰進程的堆棧跟蹤。以下是前面顯示的測試應用程序的崩潰日志,該日志使用Core Foundation讀取了惡意屬性列表。
閱讀這篇文章以了解Core Foundation如何處理無法識別的選擇器異常并闡明堆棧跟蹤中的_CF_forwarding_prep_0會很有幫助。有了這些信息,我將之前的返回地址視為CFStringFind中的異常的可能來源,尤其是在調用_CFStringGetLength之后。下面的反匯編說明了此調用:
CFString查找反匯編
我逐步完成了LLDB中的CFStringFind,直到調用_CFStringGetLength來檢查寄存器之前。從蘋果的_CFStringGetLength文檔中,我們知道第一個參數應該是字符串,因此我們可以使用以下LLDB命令檢查RDI寄存器。

可以看到,第一個參數的對象類型不是字符串,而是惡意bplist中的_NSDate對象。下面的_CFStringGetLength的反編譯說明了可能會出錯的地方:
_CFStringGetLength反編譯
我們可以看到,在該函數的第一個參數上調用了長度選擇器,我們知道該函數對于_NSDate對象將失敗,因為它沒有此選擇器,該理論也與崩潰日志相匹配。

如果我們繼續執行這個函數,則會最終將在Objective-C異常處理的內部遇到一個異常,這表明我們已找到這些崩潰的根本原因。
其他選擇器
繼續生成帶有非字符串對象的bplists,并且能夠從其他無法識別的選擇器在Core Foundation中生成其他崩潰。下面的崩潰日志是LSD在使用了一個惡意bplist和一個剩余的nscfdata對象后的崩潰日志:

此LSD崩潰的堆棧跟蹤如下:

注意,在Objective-C異常處理之前,崩潰的位置不是來自CFStringFind,而是實際上調用_CFStringGetCStringPtrInternal的CFStringFindWithOptionsAndLocale,最終由于調用錯誤的選擇器_fastCStringContents而崩潰。這樣做的原因是,剩余的nscfdata類型實際上有一個長度選擇器,所以它成功地通過了我們之前看到的第一個崩潰位置,并進一步進入Core Foundation,直到它調用另一個未識別的選擇器。
多個漏洞的發現
在此研究的早期,我使用plutil從惡意bplist生成崩潰,然后編寫了自己的代碼以到達必要的代碼路徑。以下命令通過使用plutil作為目標進程和print plist標志來設置一個LLDB會話,以開始調試此崩潰,該標志只會打印出人類可讀版本的屬性列表。

經過幾次執行之后,很明顯plutil實際上在其他位置崩潰,而不是在Core Foundation中崩潰。下面的輸出說明了它試圖在__NSDate類型上調用長度選擇器,該類型會導致無法識別的選擇器異常,但是此漏洞存在于plutil中,而不存在于Core Foundation中。
似乎在許多macOS應用程序中存在類似的漏洞,這些漏洞假定bplists僅包含字符串對象類型。 LSD進程崩潰的堆棧跟蹤如下所示:

如果我們使用GHIDRA來反匯編_LSPlistCompactString函數,則可以看到偏移量45或0x2D導致我們又對漏洞對象類型進行了另一個長度調用,大概是從我們現在在LSD數據庫中的惡意bplist調用的:

我們可以通過在_LSPlistCompactString上設置斷點并使用以下斷點命令打印第一個參數來驗證這一點:

下面的輸出說明LSD正在從惡意bplist獲取__NSDate對象:

這證實了我最初認為的一個漏洞實際上是多個macOS二進制文件中的許多漏洞,并且所有漏洞均源于bplist僅包含字符串對象的假設。
惡意影響
- 根級進程可能會在普通用戶帳戶中崩潰,并且如果操作系統重新生成了根級進程,則它們會反復崩潰(例如,LSD和MDS)。
- 系統不穩定和拒絕服務特別是在Finder或其他與UI相關的進程消耗了惡意bplist并使崩潰進程需要崩潰0-click時發生,因為應用程序捆綁包,程序包等在寫入磁盤時會自動進行處理。
- 有可能使普通用戶帳戶中與安全相關的進程崩潰,從而刪除安全邊界(XProtect等),盡管本文中并未對其進行全面探討。
- 在自動分析bplist的情況下有可能被遠程利用。
- 受此漏洞影響的系統組件包括使用Core Foundation解析bplist的應用程序,占很大的比例,在macOS 10.15.3上快速搜索了1000多個已安裝的二進制文件,這些文件導入了實現此漏洞的函數。
- 許多應用程序通過Core Foundation解析bplist數據,但也會在自己的代碼中錯誤地訪問生成的對象,這意味著漏洞發生的概率可能更大。
本文翻譯自:https://objective-see.com/blog/blog_0x5A.html
【責任編輯:趙寧寧 TEL:(010)68476606】