Many people who jailbreak their devices are unaware of the vulnerabilities being exploited in order to gain privileged access to the underlying iOS operating system. Users typically jailbreak devices in order to install applications that have not undergone Apple’s software evaluation process. This post will explore the low level mechanics of the iOS 9.3.3 jailbreak as an educational case study.
There are many restrictions in place that are enforced for applications and users. A Jailbreak ultimately requires a bug in the kernel that can be exploited. Restrictions include KASLR, SMAP, KPP and the App Sandbox. In order to gain access to the kernel, there are typically multiple software flaws and misconfigurations that must be leveraged, which then leads to access as a privileged and unrestricted user.
Overview of iOS Security Architecture
Below is a high level diagram of the iOS Security Architecture that demonstrates security controls that are in place between the hardware/firmware and software.
The App Sandbox is designed to ensure that apps are doing what they’re supposed to do. It is also there to protect applications from unintentional bugs that may be introduced through flaws in the code or inherited from a framework. Contrary to an application without App Sandbox, an application with App Sandbox limits the resources on a per app basis to protect from such flaws. App Sandbox is there as a last line of defence against various attacks that can be utilized to gain access, delete or corrupt data pertaining to the targeted application. The image below depicts how an application is protected using the App Sandbox.
XPC
XPC is an advanced framework that is built on Mach messages and simplifies low level Inter-process Communication (IPC). XPC allows communication between Application and System services. These messages are passed between an XPC Server and XPC Client. XPC is widely used by system frameworks and first-party applications. You can run the following command to survey the inventory of XPC services that are on a given iOS or OS X system:
find /Applications -name \*.xpc
Exploiting Userland vulnerability in assetsd via XPC message
Now we understand some of the various technologies used to mitigate certain issues targeting iOS applications, as well as some of those that will be used as an exploit vehicle to trigger Userland
code execution. Let’s take a look at how the iOS 9.3.3 Jailbreak works under the hood.
A vulnerability exists in assetsd
that allows files and directories to be moved to a new location.
In iOS 9.3.3 container apps can communicate with a service provided by /System/Library/ Frameworks/AssetsLibrary.framework
named com.apple.PersistentURLTranslator.Gatekeeper
via XPC. There is a method that allows a user to move a specified file or directory in /var/mobile/MEDIA/DCIM
. The problem is that srcPath
and destSubdir
are derived from user input retrieved in XPC messages which lack validation. It is possible to use commonly known path traversal tricks such as ../
in the srcPath
and destSubdir
parameters which lead to arbitrary file reads/writes as the iOS mobile
user.
This is what a sample XPC message that triggers the issue in assetsd would look like:
// code snippet – thx Pangu xpc_connection_t client = xpc_connection_create_mac_service("com.apple.PersistentURLTranslator.Gatekeeper", NULL, 0); xpc_connection_set_event_handler(client, ^void(xpc_object_t_response) { }); xpc_connection_resume(client); xpc_object_t_ dict = xpc_dictionary_create(NULL, NULL, ); NSString *dstPATH = [@"../../../../../../../" stringByAppendingPathComponent:dest]; xpc_dictionary_set_string(dict, "srcPath", [src UTF8String]); xpc_dictionary_set_string(dict, "destSubdir", [dstPath UTF8String]); xpc_dictionary_set_int64(dict, "transactionID", 4); xpc_dictionary_set_int64(dict, "operation", 4); xpc_object_t reply = xpc_connection_send_message_with_reply_sync(client, dict);
The issue is triggered on line 10.
Utilizing dyld (Dynamic Linker) to Get Arbitrary Code Execution
To inject a dylib
into a system process, an attacker can utilize the DYLD_INSERT_LIBRARIES
environment variable, but the executable must have the get-task-allow
entitlement. The Pangu team checked all executables in iOS 9 and did not identify one that had the get-task-allow
entitlement. They were excited to find that the developer disk images (DDI) did allow this by running the following command:
codesign -d --entitlements - .//usr/libexec/vpnagent
That command above produces the following output which proves that vpnagent
has the entitlement needed:
<plist version=1.0> <dict> <key>get-task-allow</key> <true/>
In order to make this executable, the old Developer Disk Image (DDI) that contains vpnagent
should be mounted. Even though a failure will occur, MobileStorageMounter
will register the trustcache hash values for executables which is signed by Apple. MobileStorageMounter
will then notify the kernel that vpnagent is a platform binary without creating any code signing failures on iOS 9.
The kernel enforces the sandbox profile for a particular executable in a couple of different ways. First, the default container sandbox profile will be applied if the vpnagent
executable is located in /private/var/mobile/Containers/Data/
. If the executable is located somewhere else on the system, the kernel will apply the seatbelt-profile, which is specified in the executable’s signature segment. A sample of what one may look like is as follows:
(version 1) (debug allow) (allow process*) (deny default)
Enabling Debugging
In order to enable debug server on iOS 9 a normal DDI should be mounted.
Utilizing assetsd Path Traversal XPC Vulnerability to Execute Arbitrary Code
The next step is to send a specifically crafted XPC message to exploit a path traversal vulnerability in assetsd
to move the vpnagent
from the DDI to a place that the debugserver
has access to. Once the executable is moved to a path outside of /private/var/mobile/Containers/Data
, the sandbox seatbelt-profile will be applied. This will ensure that the kernel does not apply the default sandbox profile.
Putting the VPN Agent in Debug Mode and Performing Code Injection
Once this has been performed, the debugserver
will allow a process with the get-task-allow
entitlement to continually run even if code signing invalidation occurs. A dylib
can now be injected using the DYLD_INSERT_LIBRARIES
environment variable. DYLD_INSERT_LIBRARIES
is very similar to LD_PRELOAD
on Linux. The signature of a system binary should be used when loading the dylib
so that the kernel will believe that the vpnagent
is loading an iOS 9 system binary.
Below is sample code that demonstrates simple code injection on OS X:
#import "ACCalculatorOverrides.h" #include <stdio.h> #include <objc/runtime.h> #include <Foundation/Foundation.h> #include <AppKit/AppKit.h> static IMP sOriginalImp = NULL; @implementation ACCalculatorOverrides +(void)load { // We replace the method -[CalculatorController showAbout:] with the method -[ACCalculatorOverrides patchedShowAbout:] Class originalClass = NSClassFromString(@"CalculatorController"); Method originalMeth = class_getInstanceMethod(originalClass, @selector(showAbout:)); sOriginalImp = method_getImplementation(originalMeth); Method replacementMeth = class_getInstanceMethod(NSClassFromString(@"ACCalculatorOverrides"), @selector(patchedShowAbout:)); method_exchangeImplementations(originalMeth, replacementMeth); } -(void)patchedShowAbout:(id)sender { // We first call the original method to display the original About Box sOriginalImp(self, @selector(showAbout:), self); // Run our custom code which simply display an alert NSAlert *alert = [NSAlert alertWithMessageText:@"Code has been injected!" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"The code has been injected using DYLD_INSERT_LIBRARIES into Calculator.app"]; [alert runModal]; } @end
The following command will build this dynamic library:
gcc -framework AppKit -framework Foundation -o CalculatorOverrides.dylib -dynamiclib ACCalculatorOverrides.m
The final step is to inject it into the application:
DYLD_INSERT_LIBRARIES=/PATH_TO/CalculatorOverrides.dylib/Applications/Calculator.app /Contents/MacOS/Calculator &
This will result in the following:
As shown above, an alert box was injected. This will be utilized in a similar fashion to exploit the next series of vulnerabilities that will allow a user gain access to the device as a privileged user.
Conclusion
Most users are unaware that Jailbreaking devices requires the exploitation of security flaws and configuration weaknesses that exist on a particular version of iOS or an application running on their device. These same vulnerabilities can be exploited by those with real malicious intent. Even then, considering the Jailbreak teams do not provide complete source and steps required to jailbreak, it is hard to tell everything that they may be doing. Further details will be covered in the next blog post.
[Part 2 – To be Continued]
References
- Pangu Internals – https://www.blackhat.com/docs/us-16/materials/us-16-Wang-Pangu-9-Internals.pdf
- Jonathan Levin. (2013). Mac OS X and iOS Internals. New York, NY: John Wiley & Sons
- Amit Sing. (2007). Mac OS X Internals. New York, NY: Addison-Wesley
- Jailbreak Exploits – https://www.theiphonewiki.com/wiki/Jailbreak_Exploits
- iOS Security – iOS 9.3 or later – https://www.apple.com/business/docs/iOS_Security_Guide.pdf
- Compromising IDEVICES via Airdrop – https://2015.ruxcon.org.au/assets/2015/slides/ruxcon-2016-dowd.pptx
- Simple code injection using DYLD_INSERT_LIBRARIES environment variable – http://blog.timac.org/?p=761
- App Sandboxing – https://developer.apple.com/app-sandboxing/
- osx dylib injection – https://github.com/scen/osxinj