來源鏈接:阿里聚安全博客
作者:蒸米@阿里移動安全
0x00 序
這段時間最火的漏洞當屬阿聯酋的人權活動人士被apt攻擊所使用的iOS PEGASUS(又稱Trident三叉戟)0day漏洞了。為了修復該漏洞,蘋果專門發布了一個iOS 9.3.5版本。 這個漏洞的厲害之處在于可以直接從沙盒內對內核進行攻擊(無需沙盒逃逸),并且同時影響iOS(9.3.4)和OS X (10.11.6)。因此,==本篇文章將會從PEGASUS漏洞形成的原因開始分析,然后一步一步教大家寫出OS X上利用PEGASUS提權的攻擊代碼。==
《iOS冰與火之歌》這一系列文章的目錄如下:
- Objective-C Pwn and iOS arm64 ROP
- 在非越獄的iOS上進行App Hook(番外篇)
- App Hook答疑以及iOS 9砸殼(番外篇)
- 利用XPC過App沙盒
- UAF and Kernel PWN
- 基于PEGASUS(三叉戟)的OS X 10.11.6本地提權 (番外篇)
另外文中涉及代碼可在我的github下載: https://github.com/zhengmin1989/iOS_ICE_AND_FIRE
0x01 漏洞分析
PEGASUS(三叉戟)實際上是由三個漏洞組成,分別是:
CVE-2016-4657:Safari的Webkit內核上的內存破壞漏洞。 CVE-2016-4655:內核信息泄露漏洞,可用于繞過KASLR。 CVE-2016-4656:內核UAF漏洞,可用于控制內核并執行任意代碼。
關于CVE-2016-4657目前還沒有公開的資料,但Stefan Esser和Pangu分別爆出了另外兩個漏洞CVE-2016-4655和CVE-2016-4656的細節。利用已經曝光的這兩個漏洞,其實已經可以做到iOS非完美越獄和OS X本地提權了。下面我們就來講解一下這兩個漏洞形成的原因以及如何利用。
0x02 CVE-2016-4655 內核信息泄露
CVE-2016-4655這個漏洞形成的原因是內核在序列化和反序列化OSNumber的時候沒有驗證長度的正確性。因此,如果我們將number的長度設置的非常長,并用io_registry_entry_get_property()去獲取number數據的話,就會造成內核的信息泄露。

我們知道內核棧中會保存函數的返回地址,因此我們可以利用這個返回地址去計算出內核的kslide,從而攻破kaslr的保護。 那么如何編寫利用代碼呢?我們先創建一個序列化后的dictionary。對內核來說,這個dictionary應該是這樣的:
<dict>
<key>min</key>
<number>0x4141414141414141</number>
</dict>
但是我們對OSNumber的長度進行了修改,變成了0x200:
uint32_t data[] = {
0x000000d3,
0x81000001,
0x08000004, 0x006e696d,
0x84000200, //change the length of OSNumber
0x41414141, 0x41414141
};
發送這個給內核后,內核在反序列化的時候就會出現錯誤。隨后我們使用io_registry_entry_get_property_bytes()這個用戶態函數就可以獲取到內核返回的數據了。

因為我們修改了OS number的長度,所以返回的數據不光有我們發送給內核的number,還有棧上數據,比如函數ret時候的返回地址-0xFFFFFF80003934BF。

通過這個地址我們就可以計算出來kslide了。
0x03 CVE-2016-4656 內核代碼執行
CVE-2016-4656這個漏洞其實有兩種觸發UAF的方法,我們這里先講比較簡單的那一種(兩種方法在Stefan Esser的文章中都有介紹)。簡單UAF漏洞形成的原因是OSUnserializeBinary支持用OSString和OSSymbol來作為key,并且支持用OSObject去引用之前的key。但是OSString和OSSymbol不一樣的地方在于,OSString key轉換為OSSymbol的過程中OSString已經被free掉了,但這個OSString卻被加入了對象列表里。

因此當我們OSObject類型去引用一個已經被釋放了的OSString的時候,就會產生UAF崩潰:

通過匯編崩潰的位置我們可以找到源碼對應的位置是在341行創建OSObject對象的時候:

因此,如果我們能夠在OSString被free的時候,立刻申請一段和OSString一樣大小的內存并且構造好對應的vtable數據,當程序執行到OSObject創建的時候,內核就能成功的被我們控制。
那么如何編寫利用代碼呢?我們還是先創建一個序列化后的dictionary。對內核來說,這個dictionary應該是這樣的:
<dict>
<string>A</string>
<bool>true</bool>
<key>B</key>
<data>vtable data...</data>
<object>1</object>
</dict>
內核隨后會解析這個dictionary,正如我們之前分析的,OSString-”A”在創建完后就被free掉了,這時候,我們立刻創建OSSymbol-”B”以及和OSString-”A”大小相同的OSData,就可以在OSString-”A” free后重新控制這塊內存,隨后當內核使用OSObject引用OSString-”A”,并調用retain()函數的時候,其實就是在調用我們已經控制的vtable了。
0x04 利用ROP提權
首先我們先申請一塊內存來放vtable和ROP chain,在OS X上有一種取巧的方法,如果我們是32位的程序的話,可以使用NULL page。因此,我們先用vm_allocate()申請到NULL Page,然后將vtable和ROP chain都保存在NULL page里:

隨后在OS X上用rop提權的代碼我們可以直接使用tpwn的:首先獲得當前進程的ucred,然后將cr_svuid設置為0,最后用thread_exception_return退出進程。

0x05 測試EXP
編寫完代碼后,我們來嘗試執行一下我們的exp。
首先說一下測試環境:Mac OS X EI Capitan 10.11.6 (15G31),在沒有安裝2016-01的security update的情況下(這時候內核相當于iOS 9.3.4,如果安裝完2016-01 update就相當于iOS 9.3.5)。

接下來我們編譯一下我們的exp:
clang -framework IOKit -framework Foundation -framework CoreFoundation -m32 -Wl,-pagezero_size,0 -O3 exp.m lsym.m -o exp
然后運行:

可以看到我們已經成功的獲取了root權限。
0x06 總結
這篇文章介紹了如何利用PEGASUS(Trident三叉戟)做到內核信息泄露以及內核代碼執行,然后利用rop獲取root權限。另外,因為PEGASUS(Trident三叉戟)同時存在于iOS和OS X,有興趣的同學可以在我們發布的攻擊代碼的基礎上,嘗試一下iOS攻擊代碼的編寫。
基于PEGASUS(Trident三叉戟)的OS X 10.11.6本地提權exp的下載地址: https://github.com/zhengmin1989/OS-X-10.11.6-Exp-via-PEGASUS
0x07 參考資料
- http://blog.pangu.io/cve-2016-4655/
- https://sektioneins.de/en/blog/16-09-02-pegasus-ios-kernel-vulnerability-explained.html
- https://bazad.github.io/2016/05/mac-os-x-use-after-free/
- https://github.com/kpwn/tpwn
作者:蒸米@阿里移動安全,更多安全類技術文章,請訪問阿里聚安全博客
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/56/
暫無評論