iOS testing is still very confusing for many hackers, and there is a lot of outdated information that makes understanding difficult. This guide does not claim to contain the perfect or the only way to test on iOS, but I can’t deny that it has been very successful for me.

The mandatory tools needed to perform iOS testing are as follows: 

  • A jailbroken iOS device
  • The OSX system.

If you don’t know what a jailbreak is yet, don’t worry—I’ll provide an explanation in the following. 

Linux users will encounter some limitations and roadblocks along the way, making testing a bit harder. If you’re working in the Apple ecosystem, try to stick with the same environment used for native development. It will make testing much easier for you.

 

This is iOS

First, let’s take a look at iOS. iOS is a mobile operating system developed by Apple. Its core structure is based on OSX, which is the operating system on Apple’s desktop products. iOS has been used in all iPhone and iPad devices since the very beginning of Apple.

The iOS and OSX kernel is called XNU, which is an acronym for “X is Not Unix.” XNU is a development component that Apple acquired together with NeXT in 1997. It was developed as the kernel of NeXT’s NeXTSTEP operating system, which later effectively became OSX. Other well-known Apple-specific technologies, like Cocoa and Objective-C, originated here too. If you deal with iOS apps, you will have noticed that many system APIs are prefixed with “NS”—this is also a legacy of NeXTSTEP.

The XNU kernel is a combination of the Open Software Foundation Mach Kernel (OSFMK), FreeBSD parts (POSIX layer, network stack, and much more), and a C++ API called IOKit. Therefore, many parts might look familiar, especially in the userland. Even if “X is not Unix,” the many parts inherited from FreeBSD makes numerous things easier for users and developers with *NIX experience. This also applies to iOS, especially on jailbroken devices. 

iOS is very uniform. This is to ensure a consistent user experience across all iOS devices. In other words, there are only minor differences that exist in the driver landscape, but these are out of the scope of this article. The one slight outlier is iPads because they have slightly different features and a more iPad-sized user-friendly experience. Apple TV devices also differ slightly, but this discussion is also out of scope. This business model is highly unlike that of Android, which comes in various versions that can be customized by each vendor or the community. 

While the iOS kernel and some libraries are open source, most of the other parts of iOS are not. This makes research into the system more difficult. But since we are focusing on testing apps, this shouldn’t hinder us too much because the API is well-documented. Just be prepared to deal with binary targets at the app level and jailbreaking.

 

Jailbreaking

A jailbreak is an exploit that uses an existing vulnerability to break out of the sandbox (jail) and obtain unprotected root-level access to a device. 

Jailbreaking started with simple symlinks that pointed out of the chrooted sandbox. This simple trick defeated the sandbox security in the very early iOS versions. As iPhones gained popularity, jailbreaking evolved toward complex heap exploits. Modern jailbreak exploits are unable to maintain persistence after reboots at present. That’s why they are called tethered jailbreaks. If a jailbreak exploit can keep persistence, it is called an untethered jailbreak.

Apple has gone to great lengths to lock iOS security down at a maximum level, improving the mitigations against jailbreaks and other exploits with every release. Exploit writing has become much more difficult over the years, and as a result, jailbreaks are becoming increasingly rare, especially for newer versions of iOS. 

Another limiting factor is the growing exploit market. Jailbreak exploits sell for insane amounts of money, which makes public releases unlikely. Only publicly exposed vulnerabilities remain an option. However, Apple quickly blocks all downgrade options to vulnerable versions after an update has been released. 

Therefore, it is important to obtain and maintain jailbroken devices for research. The userland access to locked devices is so limited that proper testing is not possible with such devices. 

At the time of writing, checkra1n is the best-developed and widest-spread jailbreak solution. It supports up to iOS 14. If a device has been updated to a newer version of iOS, it is usually difficult or even impossible to downgrade it in a proper way. Make sure your device is supported and has a proper iOS version before proceeding. 

I use an iPhone 7 with iOS 13.8, which works just fine. Unless your iOS version becomes outdated, the described setup should work. Most apps are compiled to be compatible with older iOS versions, so you can always use an old iOS device for testing. The iPhone 6s is an affordable option and often comes with old-enough iOS versions for jailbreaking.

In the event you can’t access a physical device, a virtual Corellium device might be an option. Corellium offers virtualized iOS devices in the cloud. Since it is an emulation, jailbreaks up to the latest iOS version are possible. Sadly, hardly any up-to-date tools for these versions exist, making testing on these devices harder. It’s generally recommended to use iOS versions widely supported by the community. Later versions should only be used for special cases or if required.

 

Setup

After jailbreaking your iOS device with checkra1n, you will find Cydia preinstalled. Cydia is a free package manager for external, non-App Store software (think of it as an unofficial app store). Cydia is based on APT. To set up the device, please update all installed packages and install the following repositories:

To start Cydia, click Manage -> Sources -> Edit -> Add.

After this, install the following packages to your device:

  • OpenSSH
  • AppSync Unified
  • Frida 

What do these packages do?

  • OpenSSH allows you to log in to the shell via the network. It is also used during unpacking.
  • AppSync Unified allows you to install iPhone application packages (IPAs) with a broken signature (i.e., IPAs that are provided as developer builds without a valid profile).
  • Frida is a binary instrumentation framework; it can be used for debugging, but we will use it for decryption later.

After installing these packages, you should be able to log in to SSH using the following default iOS credentials:

  • User: root
  • Password: alpine

These have been the same for every iOS version since Apple’s very beginning. This is most likely because the root user is disabled by default, and there is no point in changing it. 

For a research device, these credentials shouldn’t be changed either. Please make sure you don’t use your research device in an area with public Wi-Fi or as a regular phone. Such actions can put you at high risk of being compromised. Maintain the research device as a dedicated tool only for study purposes.

 

iOS system paths

Once you have logged into the system, here are some important paths you should know: 

App Store applications are installed into this directory:

/var/containers/Bundle/Application/<UID>/

Data directory

/var/mobile/Containers/Data/Application/<UID>/

Shared data directory

/var/mobile/Containers/Shared/AppGroup/<UID>/

The paths contain the variable part <UID>. This is a unique ID generated upon the installation of a packet. It is not static across devices and changes with every reinstallation. To find the last-installed packet, look at the creation dates of the folders. Since OpenSSH is installed, it is very convenient to use SFTP to browse the file system. Filezilla has proven to work fine for this task.

 

iPhone application packages (IPAs) and app bundles

iPhone applications are delivered as IPAs. This is the iOS equivalent to an APK on Android. IPAs are essentially compressed ZIP files that contain a folder called “Payload,” which contains the app. You can use the unzip command present on every macOS computer to unzip these files.

unzip myipa.ipa -d <outputfolder>

Unlike Android APKs, Apple chooses to encrypt IPAs, or at a minimum, their binary content. You cannot disassemble the binary of an App Store IPA without decrypting it. It’s also very difficult to obtain an IPA from the App Store directly. Therefore, app decryption techniques should be used. We’ll cover this later in the blog post.

Once you have unpacked an IPA, you will be presented with an app package. It will look like any other Mac app on macOS, but it won’t be executable. If you look at apps on other operating systems, you will notice that they are just folders with a .app extension. iOS apps are not monolithic. They are sets of various files in a folder. Apple calls this a package or bundle. OSX will automatically detect the .app extension and display the icon and name of the app instead of a generic folder.

To view the content of a package on macOS, right click -> show package content.

The structure of a package is outside the scope of this blog post, but Jonathan Levin goes over it in detail in the Mac OS X and iOS Internals series. These books are highly recommended to anyone who wants to dive deeper into iOS. Another great read is the iOS Hackers Handbook by Charlie Miller et al.

 

Decrypting iOS apps

There are many tools out there that promise to decrypt files. In the past, many on-device tools were used. Sadly, most stopped working after certain iOS updates. The tools ended up abandoned and have thus never been updated.

One tool that hasn’t seen any recent updates but has at least worked for many years is frida-ios-dump. It is based on Frida. Frida is the binary instrumentation framework we installed earlier. Since the Frida server is very well maintained, the iOS-linked parts of the tool are not likely to become outdated.

Using frida-ios-dump is straightforward. A detailed and clear explanation can be found on the GitHub page. It requires another support tool called iproxy. This tool can easily be installed via homebrew as part of the usbmuxd package. There is a way to work without iproxy, but it is not recommended.

Install iproxy:

  • brew install usbmuxd

The most common commands for Frida iOS dump are as follows:

  • python dump.py -l
  • python3 dump.py com.yourpacked.ident

com.yourpacked.ident is a packet identifier that is part of the info.plist within the application package. The package identifier is chosen by the developer during app development. To get the right identifier for your target package, use the list command (-l) provided by dump.py.

 

Signatures

All IPA packages and binaries are signed. On a jailbroken phone, the signature validation is still partially intact, so unsigned code or code with bad hash sums in the header will not run. If a binary is not properly signed, you will encounter a signing error (Error Killed 9). You can test this by running the executable directly from the command line of the phone if an app refuses to start. This is not a normal way to execute an iOS executable, but it is good enough to validate if a signature is broken or if another problem exists.

If a signature is broken, the ldid command line can be used to fix it:

ldid -S <binary>

ldid will add correct signatures to an executable binary and keep the entitlements in place. This will allow the app to be executed by the iOS kernel. This is done on the device and is just a fix after quick changes on the device. It is not intended to “resign” an entire app. If you need to resign an entire app, use iOS app resigner or another similar tool.

To install a fake-signed or developer-signed binary, AppSync Unified can be used. It allows you to install IPAs with any kind of signature. This is very useful for research purposes because IPAs for testing are often delivered with developer signatures and you don’t need to install additional provisioning profiles.

To install any IPA to your device, you can use Apple Configurator. Apple Configurator is free and normally used for enterprise device management, but it is also very convenient for installing IPAs on a research device.

 

Proxies

Using Burp Suite with iOS is not as straightforward as on other platforms. The certificates generated by Burp do not meet the iOS minimum requirements

To use Burp Suite correctly, a new certificate chain including Certificate Authority (CA) must be generated. This can be done with the following commands:

Generate a private key:

openssl genrsa -aes256 -out ResearchCA.key 4096

Generate a certificate with the required extensions:

openssl req -x509 -nodes -newkey rsa:4096 -keyout ResearchCA.key -out root-ca.crt -days 365 -subj “/C=CA/O=Burp/OU=Certification Services/CN=ResearchCA/” -addext “extendedKeyUsage=1.3.6.1.5.5.7.3.1”

Export to pkcs12 format for import into Burp:

openssl pkcs12 -export -out ResearchCA.pfx -inkey ResearchCA.key -in root-ca.crt

Do not edit the proxy listener to install this; this chain must be installed as a CA. Within Burp, navigate to Proxy -> Options -> Import / Export CA certificate to install it.

The certificate must be trusted on the iOS device as well. To do that, follow the old Port Swigger guide. Don’t mind the out-of-date note; it still works. This is most likely due to the situation described earlier.

Burp’s mobile assistant looks interesting, but it has never really worked for me. Instead, use proxytool to switch the proxy via the SSH command line. After that, it just works. The command lines are as follows:

Set a proxy:

proxytool 1 <burp ip> 8080

Clear proxy settings:

proxytool 0

 

SSL pinning

A common roadblock in iOS testing is Secure Sockets Layer (SSL) pinning. Most developers use common implementations for validating certificates that are based on standard iOS APIs. Most of these techniques can be defeated using a tool called ssl-kill-switch2. This tool has been around for a long time and works really well. But beware, this tool should not be run on a production device either. It will break an important layer of security.

To test if SSL pinning works, just turn the tool off in settings. In some rare cases, ssl-kill-switch won’t work. If this happens, reverse engineering and the development of custom patches is required. This issue is rare, but if it does happen, these solutions must be carried out individually for the specific app.

I have only ever spotted such pinning approaches in payment or health care apps.

 

Syslog checking

The easiest way to read the syslog on iOS is by using a tool called ios_deploy. The tool is command line-based. Syslog can be dumped with the following command:

ios_deploy syslog

It is part of IDA Pro, which we will discuss later. A valid support subscription for IDA Pro is required to download it. 

Another way is using XCode, but this approach isn’t always suitable for research purposes because XCode tries to filter the syslog for the current project.

 

Binary analysis

One last important topic for iOS testing is binary analysis. Unlike for Android, the code of an iOS app cannot be easily decompiled to source the code. You must use a tool like IDA Pro to reverse engineer the application. When it comes to iOS, IDA Pro is the best tool for this purpose. 

The code generated by the included decompiler is much harder to read than the original code, but it works. This requires proper reverse-engineering techniques and is crucial for proper iOS testing. 

iOS apps are natively written in Swift nowadays; older Apps were written in Objective-C. One challenge you will face while reverse engineering Apple-based binaries is the high level of abstraction. The code regularly uses exceptions and messaging, making the code hard to follow in static analysis.

To completely understand an iOS application, debugging is required. Debugging can be done either with lldb or IDA Pro. IDA Pro is the much easier tool to use here. A basic guide on how to debug iOS is available on the Hex-Rays website. Follow this guide to install the debug server on your device and learn the basics behind how debugging with IDA Pro works.

To debug applications that are not available in source code, a change in the entitlements of the debug server is needed. Entitlements are permissions an application has, and they are permissions for various things on iOS. An important one for debugging is debugapplications. The process of changing the entitlements of the debug server is outlined in the iphonedev.wiki

You won’t be able to debug target binaries decrypted from the App Store if you skip this step. To properly debug your target apps, you must also replace the binary on the phone with a “resigned” decrypted version. ldid works great for fixing the signing of the binary for debugging purposes.

After completing these steps, you should find it possible to debug iOS apps on the device. It is strongly advised to connect via iproxy using a USB connection instead of a normal TCP connection. USB will be significantly faster and more reliable. We already discussed iproxy during the decryption process. Use the same steps to forward your designated debugserver port.

There are other tools for basic binary analysis on OSX like otool, but due to the complexity of iOS, IDA Pro is highly recommended.

Aside from binary targets, you will encounter an increasing number of web-based apps. Developers choose web technology as a platform to ease cross-platform development. These apps are often based on frameworks like React. The core of these apps can be tested like normal web apps. The container is tested like any other binary.

Another technology to keep an eye on is Flutter, which was created by Google. Due to its unique compile process and ways of referencing symbols, it is quite difficult to reverse engineer. Recently, a lot of security research was done to better understand how to reverse engineer it. I expect new ways to test Flutter will emerge as it continues to gain popularity across the developer community

 

One more thing

Sadly, the available documentation on iOS is quite often outdated. Furthermore, due to the many changes across the platform and tools, the documentation is quite difficult to understand and apply. The information in this guide should be enough to gain a basic understanding and create a working test environment. 

iOS is a proprietary platform that plays by its own rules. But once you familiarize yourself with it, it’s no more or less difficult than any other platform. I highly recommend diving into it: iOS is a very interesting world to discover. Make sure you check out the OWASP Mobile App Security Checklists. They offer numerous useful hints on where to look for bugs in iOS apps.

If you have questions about the process, feel free to hit me up on Twitter. Feedback is always appreciated. And learn more about me in my Spotlight.

______________________________________________________________________________________________

About the author

My name is Alexander Pick. I am a professional security researcher specializing in the fields of mobile, embedded, and automotive security. Technical security is not just my work, it is also my hobby and a way of life. I started out in the 90s, reading text files from mailboxes and hanging out on IRC. My first CVEs were awarded for bugs I published in 2000/2001. I write just for fun, curiosity, and purely educational purposes. A lot of time has passed, and the industry has changed a lot. More than 20 years later, I found the Bugcrowd platform and was instantly hooked. Now, I am ranked in the top 100. I am still slaying bugs, still having fun.