Request a Demo Contact Us

A Basic Guide to iOS Testing in 2022

We will have a closer look at Apple iOS today. iOS testing has a high barrier to entry. It is still quite mystical for many researchers and a lot of outdated information makes it hard to get into it. This guide does not claim to be the perfect or the only way to test on iOS, but it is the way I perform my testing at present. This guide will give you information about testing methods which work at the time of writing, allowing you to test modern apps right away.

Mandatory tools to perform iOS testing are a jailbroken iOS device and a macOS system. If you don’t know what a jailbreak is yet, don’t worry, just read on. 

Linux users will meet some limitations and roadblocks along the way making testing harder. If you are working in the Apple ecosystem, try to stick with the same environment used for native development. It will make many things much easier for you.

This is iOS

First let’s have a look into iOS. iOS is a proprietary mobile operating system developed by Apple. Its core structure is based on macOS which is the operating system of Apple’s desktop products. iOS was initially developed for the iPhone ecosystem. Later, adapted to universally work across iPhones and iPads.

The iOS and OSX kernel is called “X is Not Unix” or XNU. XNU is a development which 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 notice many system APIs 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 etc. and a lot more) and a C++ API called IOKit. As a result a few things may look familiar, especially in the userland. Even if XNU, the many parts inherited from FreeBSD makes a lot of things easier for users and developers with *NIX experience. 

Unlike Android, which comes in various modified versions, iOS is a locked down and uniformed OS. There are many different supported iOS devices but only “one” iOS. The look and feel is strictly the same across all models. Some minor differences exist in the driver landscape, such as AppleTV and iPads but this is out of scope for this article.

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

Jailbreaking

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

Jailbreaking started with simple symlinks which pointed out of the chrooted sandbox. This simple trick defeated the sandbox security in very early iOS versions. As iPhones gained popularity jailbreaking evolved towards complex heap exploits in recent times. Modern jailbreak exploits are not able 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 a long way to lock iOS down to a maximum level, improving the mitigations against jailbreaks and other exploits with every release. Exploit writing has become much more difficult over the years. As a result jailbreaks are increasingly becoming more 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. But Apple will soon block all downgrade options to vulnerable versions after an update is released. 

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

At the time of writing checkra1n is the currently best developed and widest spread jailbreak solution. It supports up to  iOS 14. If a device is 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. 

The author uses an iPhone 7 with iOS 13.8 at the time of writing which works just fine. Unless your iOS version becomes outdated, the setup should work. Most apps are compiled to be compatible with older iOS versions. 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 to be jailbreak-able.

In case no physical device is available, 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 use 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-Appstore software (think of an unofficial app store). Cydia is based on apt. To set up the device, please update all installed packages and install the following repositories:

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 login to the shell via the network, it is also used during unpacking

AppSync Unified 

Allows you to install IPA with broken signature, i.e., IPA which are provided as developer builds without a valid profile.

Frida

This is a binary instrumentation framework; it can be used for debugging but we use it for decryption later.

After installing these packages, you should be able to login to SSH using the iOS default credentials. 

User root

Password alpine

They are the same for every iOS version since the very beginning and never changed. Most likely since the root user is disabled by default and there is no point to change it. 

For a research device, these credentials shouldn’t be changed either. Please make sure that you don’t take your research device in a public WIFI or use it as a regular phone. This puts you at a high risk of being compromised. Maintain the research device as a dedicated device only for research purposes.

iOS System Paths

Once you logged into the system you should know some important paths within the system. They are detailed below: 

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 a variable part <UID> which is a unique ID generated upon installation of a packet. They are not static across devices and change with every re-installation. To find the last installed packet, look at the creation date 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 (IPA) and App Bundles

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

unzip myipa.ipa -d <outputfolder>

Unlike Android APKs, Apple chooses to encrypt IPAs or at least the binary content of them. 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 this blog post.

Once you unpack 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. But if you look at apps on other operating systems you will notice that it is just a folder with a .app extension. iOS apps are not monolithic. They are a set 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.

The structure of a package is outside the scope of this document but very well described in the Mac OS X and iOS Internals series by Jonathan Levin. These books are highly recommended to everyone who wants to look deeper into iOS. Another great read is the iOS Hackers Handbook by Charlie Miller et al..

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

Decrypting iOS Apps

There are many tools out there which promise to decrypt files. In the past a lot of on-device tools were used. Sadly, most of them stopped working after some iOS updates and the tools ended up abandoned and are never updated.

One tool which wasn’t updated recently but at least works for many years is frida-ios-dump, it is based on Frida. Frida is the binary instrumentation framework we installed earlier. Since 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 and well explained 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:

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

com.yourpacked.ident is a packet identifier which 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 are signed, as well as all binaries. 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 the 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. Just use the following command line:

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 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 IPA with any kind of signature. This is very useful for research purposes as IPA 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 to install IPA 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 CA must be generated. This can be done with the following commands:

Generate a private key

openssl genrsa -aes256 -out ResearchCA.key 4096

Generate Certificate with 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 above.

A word about Burp’s mobile assistant here, it looks interesting, but it never really worked for me. Instead use proxytool to switch the proxy via the SSH command line, it just works. The command lines are:

Set a proxy

proxytool 1 <burp ip> 8080

Clear proxy settings

proxytool 0

SSL Pinning

A common roadblock when iOS testing is SSL pinning.Most developers use common implementations for validating certificates which are based on standard iOS APIs. Most of these techniques can be defeated using a tool called ssl-kill-switch2. This tool is around for a long time and just works. But be aware, this is a tool not to be run on a production device either. It will just 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, in this case reverse engineering and the development of custom patches is required. This must be done individually for the specific app, but it is rare. 

Currently such pinning approaches were only spotted in payment or healthcare apps by the author.

Syslog Checking

The easiest way to read the syslog on iOS is 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 as XCode tries to filter the syslog for the current project.

Binary Analysis

One last important topic for iOS is binary analysis. Unlike Android, the code of an iOS app cannot be easily decompiled to source. You must use a tool like IDA Pro to reverse engineer the application. If 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. Practicing proper reverse engineering 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 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 the device and get a basic idea how debugging with IDA Pro works.

To debug applications which are not available in source code, a change of the entitlements of the debug server is needed. Entitlements are permissions the application has. There are permissions for various things on iOS. One important one for debugging is debugapplications. The process of changing the entitlements of the debug server is outlined on 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 to fix the signing of the binary for debug purposes.

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

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

Aside from binary targets you will encounter an increasing number of web-based apps these days. 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 upcoming technology is Flutter created by Google. Due to its unique compile process and way to reference symbols, it is quite difficult to reverse engineer. Recently a lot of security research was undertaken. I expect new ways to test Flutter will emerge as soon as it gains popularity across the developer community

One More Thing

Sadly, the available documentation on iOS is quite often outdated and due to many changes in the platform and tools, it is quite confusing. The information given in this guide should be enough for getting a basic understanding and creating a working test environment. 

iOS is a proprietary platform that plays by its own rules, but once you familiarize yourself with it, it’s not more or less difficult than any other platform. Get into it, iOS is a very interesting world to discover. Make sure you check out the OWASP Mobile App Security Checklists. They give you a lot of useful hints 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.

 


About the Author

My name is Alexander Pick, I am a professional security researcher in the field of mobile, embedded and automotive security. Technical security is not just my work, it is also my hobby and a way of life for me. 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. Just for fun, curiosity and purely educational. A lot of time has passed, and the industry changed a lot. More than 20 years later I found the Bugcrowd platform and was instantly hooked up. Now I am ranked in the top 100 here. Still slaying bugs, still having fun.

More resources

Guide

Ultimate Guide to AI Security

Read More
Datasheet

Aligning with Binding Operational Directive 20-01

Read More

Get Started with Bugcrowd

Every minute that goes by, your unknown vulnerabilities leave you more exposed to cyber attacks.