Modern iOS Pentesting: No Jailbreak Needed
Noah Farmer
Sep 24, 2024
Pentesting in a post-jailbreak world - a bit of backstory
Conducting a penetration test on a mobile app for Apple’s iOS used to be pretty straightforward: jailbreak your device, install the IPA, and start dynamically breaking things. But unfortunately, it seems those days are quickly fading.
Nowadays, Apple’s relentless security enhancements and lack of community support (at least compared to circa 2016) have made jailbreaking a relic of the past, especially on the latest devices and iOS versions.
This leaves us with a burning question and a bit of a problem - how do you test an application’s security when the traditional methodology is no longer an option?
A couple of months back, I was in a bit of a tricky situation. We were contracted to conduct a black-box penetration test of a mobile app where the client had given us nothing except for the App Store URL. So, let’s have a look at getting this app pentested.
First thing we’re going to need to do, is obviously get the app onto our device. Let’s take a look at the app on our jailbroken device (iPhone X).
Unfortunately, it looks like my iPhone X is too outdated to run the app. So, let’s take a look at workarounds.. At least this iPhone X is affected by the checkm8
exploit, so I should be able to update my iPhone X from iOS 14.4.1 to iOS 17.2 and pentest the app, right?
Not a chance. The latest iOS version for this device caps out at 16.7.x. :(
Okay, let’s take a look at potential jailbreaks for iOS 17.2+ on my main device, an iPhone 14 Pro. Unfortunately, like most people, I keep my device up to date. Just as I suspected, there are no available jailbreaks for this iPhone and software version.
It feels like I’ve hit a dead end, but the stakes are too high to stop here. Okay, so now what? The app that we need to pentest:
Doesn’t support my main pentesting device (iPhone X)
There are no jailbreaks for my personal device (iPhone 14 Pro)
So we’re stuck, right? Is this app’s security going to remain untested? Absolutely not.
There’s a new way forward, one that doesn’t rely on jailbreaks but instead leverages the very tools developers use to build and debug their apps. This is where pentesting gets creative, where we turn the tables on security, using the developer’s tools against them.
In this post, I’ll explore a different, more modern approach to iOS pentesting—a journey that starts with gaining debugger privileges, bypassing FairPlay DRM, and ends with turning my non-jailbroken device into a powerful pentesting tool. If you’re ready to push the boundaries of what’s possible in iOS pentesting, let’s dive in.
The new way forward - gaining debugger privileges
As a bit of preface, this is all thanks to a special entitlement built into iOS app signing, called get_task_allow
. In a nutshell, building an app with this entitlement means that external processes, like a debugger, can attach themselves to your app. This means that other applications can run a function called task_for_pid()
, with your app process ID as an argument, in order to gain the task port for the app. (https://developer.apple.com/library/archive/technotes/tn2415/_index.html#:~:text=and UIPasteboard sharing.-,get-task-allow,-The boolean value)
As defined in Apple's documentation, a task port is essentially a communication channel that provides access to an app’s process - think of it like a key to the app’s "control room." It enables external entities, like a debugger, to interact with and inspect the internal workings of the app. Once you have access to the task port for an application, you can perform actions such as reading and writing memory, injecting or patching threads, and modifying the app behaviour in real-time.
Coincidentally, if you take a look at how a jailbreak traditionally works on a technical level, you’ll notice that one of the first things they do is run task_for_pid()
with the pid
(process ID) of the kernel_task
(0
), meaning that the kernel’s memory can be patched and modified on the fly, allowing for a jailbreak to take place!
But, if we’re just pentesting a single app, there’s actually no need to modify the entire kernel, instead, we can just re-sign the app with the get_task_allow
entitlement to gain a “jailbreak-like” state for that single app, on any device/iOS version!
The other issue - FairPlay DRM
Unfortunately, in my case (and probably most cases), it’s not as simple as just pulling the IPA, re-signing it, and flashing it back to your device. If you go down that route, you’ll probably run into the following issue.
So why does this happen? Put simply, when a developer uploads their application to the App Store (or another distribution method), Apple encrypts their IPA with a form of Digital Rights Management (DRM), called FairPlay. When you download an app from the App Store, you receive a version of the app that is encrypted with a key tied to your Apple ID, ensuring that it can only be decrypted and executed in that specific environment.
When you resign a FairPlay-encrypted application, you essentially replace the digital signature, therefore invalidating the DRM key. And without this, the app won’t launch!
Fortunately, it’s pretty trivial to remove FairPlay encryption from iOS apps. If you have an old iOS device laying around that can be jailbroken, its as simple as installing the IPA, decrypting using your favourite tool (such as Iridium or frida-ios-dump
), and pulling it back off the device.
Seriously though, I should note that removing DRM encryption can be illegal, and I don’t condone the process of doing so. If you need a decrypted IPA, it is best to ask the developers for one. Unfortunately though this isn’t possible most of the time, either due to time constraints, or because the developers are unable or even unwilling to provide it. So without further ado, let’s get straight into removing FairPlay.
Defeating FairPlay DRM - Part 1: Obtaining the IPA
So, first things first, you have to obtain your IPA (if you already have it, skip ahead!). If the app you’re trying to pentest is only available through the App Store, you’re pretty limited in options. However, there is one method that is tried and trusted, and always seems to work pretty well for me.
On your Mac, launch Apple Configurator. It is an official Apple application, and you can obtain it via the Mac App Store (https://apps.apple.com/au/app/apple-configurator/id1037126344?mt=12).
Once opened, you should see your iOS device. If it’s not there, try connecting it via USB. Double-click on your device, and then click Add + → Apps from the top menu bar.
Next, open Terminal on your Mac, and cd
to /Users/[username]/Library/Group\\ Containers/K36BKF7T3D.group.com.apple.configurator/Library/Caches/Assets/TemporaryItems/MobileApps
. The IPA will appear in this folder shortly, so we’ll keep it open for now.
On your iPhone, ensure you have the app installed that you want to pentest. This isn’t a required step, but it makes grabbing the IPA much easier, as you’ll see later on. Back in Configurator, you can search for the app that you want to obtain the IPA for. Select it, and then click Add from the bottom right corner.
After clicking Add, Configurator will download the IPA from Apple, and attempt to push it to your device. If you followed my recommendation earlier and installed the IPA already, you should be at the following screen.
While Configurator is stuck here, go back to your Terminal, run ls
, and you should be able to locate the .IPA file directly from Apple!
Go ahead and copy the IPA out of this directory using cp
, and we can move on to the next step!
Defeating FairPlay DRM - Part 2: Decrypting the app
Now that you have the IPA, we have to address the original issue that brought us here in the first place. How can we use a jailbroken device to decrypt the IPA, if the app isn’t built for my older, jailbroken device? Fortunately, you don’t actually need to run the app in order to decrypt it! As long as you can install the app, it can be decrypted easily using numerous tweaks.
So how do we install it? Pretty simple, first thing that you’ll want to do is unzip the IPA (because IPAs are just fancy .ZIP files, fact of the day).
Inside this unzipped
directory, you’ll see a few things that we don’t really care too much about. The main file which we do care about, is the Info.plist
file, which is located inside the Payload/[appname].app
directory. This file dictates all of the major configuration items for the application, so we’ll want to go ahead and open it using Xcode (as it’s a special type of .plist
, which most text editors don’t support).
With this now open, you should be able to see quite a few configuration values for the application. Can you spot which one we’re interested in? 😄
Go ahead and change this to a value such as 10.0
, and save the file. Granted, the app likely won't run on older iOS versions in most cases, as developers usually set minimum versions for good reasons (e.g., the app relies on newer APIs). However, as we mentioned earlier, it doesn't actually need to be able to run. Take note of the bundle identifier as well, as you’ll need this later!
First, we’ll want to package up our application bundle that we just edited. Because IPAs are just fancy ZIP files, this part is actually pretty easy. You’ll just need to zip
all of the files from the root of the unzipped
directory we created earlier, into a new file with an .ipa
extension, like so.
Now that you have the IPA with no minimum version requirement, the next step is to install it on a jailbroken device. Since the IPA isn't signed, you'll need to install the AppSync Unified tweak from Cydia to prevent any “invalid signature” errors. (For instructions on installing AppSync Unified, check here: https://github.com/akemin-dayo/AppSync/blob/master/README.md#how-do-i-install-appsync-unified-on-my-jailbroken-ios-device).
To install the IPA, we'll use ideviceinstaller
(https://github.com/libimobiledevice/ideviceinstaller) with the following command:
If all went well, you should see the app icon appear on your iPhone’s home screen! Launching it won’t do anything, as it will instantly crash, but now we can utilise the Iridium tweak (available from Cydia) to dump the decrypted IPA! To do so, install the tweak, and open the Iridium application from the home screen. You should see a list of apps installed on the device. Tap on the app that you’d like to decrypt, and hit Decrypt Now.
When the process completes and the decrypted IPA has been created, you’ll be presented with an orange circle icon at the bottom right of the screen. Tapping this will open a share menu, which lets you AirDrop the decrypted IPA back to your Mac.
For the sake of readability, I’ll copy this back to the no-jailbreak-required
folder I’ve been working out of as decrypted.ipa
.
Preparing signing certificates via Xcode
If you haven’t used Xcode before, and therefore don’t have any signing profiles, it’s pretty easy to get them set up, as Apple offers free developer signing profiles for all accounts through Xcode. If you have this all set up, you can skip straight ahead to the next section 🙂.
To set up a signing profile on your system, first thing you’ll need to do is launch Xcode, and click “Create New Project…”. On the next page that opens, select iOS, select App, then hit Next.
On the next screen, configure your organisation identifier and product name to anything of your choosing. If you see “Add account…“ next to Team, you’ll need to click that button to sign into your Apple ID. Don’t worry, as you don’t require a paid developer account. Any Apple ID will work fine. Once you’ve configured this and selected your Team, click Next to create your project.
If all is well, you should be presented with a screen like the following. Finally, to configure your signing profiles, and set up your iPhone for installing developer apps, click the big play icon in the top left of the Xcode window!
If this is your first time using Xcode, you may be prompted to enable Developer Mode on your iPhone to continue. Doing so is pretty simple, just navigate to Settings → Privacy & Security, and click on Developer Mode right down the bottom. You’ll be prompted to restart your iPhone to continue.
After your iPhone restarts, go ahead back to Xcode and click that play button in the top left again. If all is well, you should see the app launch on your iPhone, meaning all our signing certificates are in the right place! I should note that you don’t have to do this setup every time you resign an app, this is just to get the signing certificates set up and your iPhone prepared to run developer apps.
Jailbreak 2.0 - Resigning with get-task-allow
Now that we've set up our iOS device with developer mode and obtained our decrypted IPA without FairPlay DRM, we can take the final crucial step. We'll enable the get-task-allow
entitlement when resigning the application, granting us debugger privileges for our application's sandbox. All that's left is to resign the app with this entitlement enabled and install it on our iPhone!
When it comes to resigning applications, you have quite a few tools to choose from, such as ios-app-signer
, codesign
, and iResign
. For this demo, we'll be using ios-app-signer
(https://github.com/DanTheMan827/ios-app-signer). I'm a huge fan of this tool because its GUI is incredibly user-friendly, and even has the added bonus of automatically detecting all your installed signing profiles.
Go ahead and open iOS App Signer after downloading it, and you should be presented with an interface like the following.
In order to resign our app with debugging privileges, you’ll need to configure iOS App Signer with the following options:
Input File: select the
decrypted.ipa
file we copied over earlierSigning Certificate: ensure the “Apple Development” certificate is selected (this should be the only option)
Provisioning Profile: select the “iOS Team Provisioning Profile” that has your new application bundle ID you created earlier in Xcode
No get-task-allow: ensure this is unticked!! ⚠️
Once that’s all done, you can press Start, and the tool will begin to resign your IPA. For this demonstration, I’ll save the resulting resigned IPA as resigned.ipa
. After the process has completed, you can go ahead and install it on your normal iPhone! I’ll go ahead and use ideviceinstaller
again to do so.
Jailbreak 2.0 - Time to hook!
Now we have the debuggable application installed on our iPhone 14 Pro running the latest iOS, let’s use some traditional pentesting tools to interact with the application! First, let’s go ahead and launch the application using objection
(https://github.com/sensepost/objection), one of my favourite iOS pentesting tools.
Thanks to recent updates in Frida (https://frida.re), the framework that Objection runs on top of, you don’t need any special arguments or setup to run your tooling. For example, to utilise Objection, all I have to do run is the following command (which is also the same on a jailbroken device).
And there it is! We’ve successfully hooked into the application. We now have a pseudo-jailbroken environment, but isolated to this single app’s sandbox. Let’s confirm our execution by running pwd
to check the current directory, and ls
to view the files within the app’s sandbox.
So, just as a quick recap, we’ve:
Downloaded an official IPA from Apple
Removed FairPlay DRM
Patched the IPA to contain the
get-task-allow
entitlement…and bypassed the need for a traditional jailbreak!
Even better, most of the tools you’re already familiar with will work seamlessly with this new method, often without additional setup or workarounds.
This new approach not only keeps pace with Apple’s tightening security measures, but also opens up new possibilities for testing in environments where jailbreaking might no longer be viable. As iOS (and mobile testing as a whole) continues to evolve, so must our methods. The tools are out there - now it’s up to us to find new, creative uses for them.