When developing apps that have the need for extra security, we often get requests to detect jailbroken phones. The reason for detection is to either disable some, or most of the app's functionalities due to security concerns that come from a jailbroken system.

In my opinion, these kinds of checks don’t contribute too much to the security side, since they can be targeted and disabled, and might just alienate some of your power users.

Let’s demonstrate how.

What isn't a jailbreak?

In order to understand what a jailbreak is, it’s best to start from what a jailbreak isn’t. By default on iOS, every app runs inside a sandbox of its own. In a nutshell, this means that every app has a very limited access to system resources, and almost no access* to other apps and their data.

Source: Apple's Sandbox Design Guide

Interested in learning more? It’s covered in the official docs.

*sometimes there are mechanisms for exchanging data between apps, that are implemented intentionally

What is a jailbreak?

If we rename sandbox to jail, then the term jailbreak makes more sense. By definition, a jailbreak is a privilege escalation for the purpose of removing software restrictions imposed by Apple on iOS, tvOS and watchOS.

In case you didn’t understand a word from above, it means giving apps admin (root) level access, which in term allows installation of other apps, tweaks and themes not on the App Store. Think Cydia - the unofficial official jailbreak software manager.

Jailbreak detection

So, now that we’re familiar with what jailbreaking isn't and what it is, it’s time to discuss how developers detect a jailbreak. While there’s no official way to detect a jailbreak, and false positives are possible, most methods rely on attempts to access resources that are usually inaccesible on a sandboxed app.

For example we will try to:

Try to find the presence of Cydia

Attempt to find existence of CydiaSubstrate, the framework that allows installation of third-party patches

Attempt to access directories that should not be available to an app without escalated privileges (such as /bin/bash, /etc/apt)

Try to find symbolic links to usually unavailable directories

Or, attempt to write to a directory where that usually wouldn’t be possible

The reason for having so many different methods is due to many different types of jailbreaks and iOS versions. Usually, if any of the methods above succeeds, we assume we’re dealing with a jailbroken phone.

Breaking the jailbreak

In order to demonstrate that detecting a jailbreak doesn’t pose a significant obstacle to a determined hacker, we’ll need a few things:

An app with jailbreak detection A jailbroken device A bit of reverse-engineering A tweak that disables the jailbreak detection for the same app

Let’s roll up our sleeves.

1. App with jailbreak detection

For the sake of this article, I’ve developed a simple app with an elaborate name Jailbreaking Bad. Its only purpose is to detect if a device is jailbroken.

If you’re interested in the source code, you can find that here:

GitHub - Adis/jailbreaking-bad

The jailbreak detection is contained inside a class named JBChecker , which is just a wrapper for a more complicated class that does all the heavy lifting, for clarity.

This class only has one method that checks if the device is jailbroken or not, and returns the result. This is also our entry point when jailbreaking the app a few steps later.

+ (BOOL)isJailbroken { // Lib used can be found at https://github.com/thii/DTTJailbreakDetection return [DTTJailbreakDetection isJailbroken]; }

After opening the app, you will be presented with a single button; tap it, and the app will tell you if it has detected a jailbreak or not.

2. A jailbroken device

Obviously, a major part of testing this yourself is getting your hands on a jailbroken device. I won’t be covering how to perform a jailbreak in this article as this info is widely available online.

In case you were wondering how the app looks like on a jailbroken iPhone, here's a handy gif:

3. A bit of reverse-engineering

In order to develop a tweak for an app, you will have to figure out the behaviour you want to change.

Depending on your level of curiosity, there are plenty of tools out there which allow you to deconstruct and observe the ins and outs of a certain app.

In order to get familiar with with our own app, first, we’ll want to inspect the IPA (not the beer, the extension) file from the device on a unix machine. The tool of our choice will be class-dump.

IPA file is just an archive, so unarchive it, and take a look at its content. You'll find icons, folders, .plist files - but we’re looking for a mach-o file that represents our app in a binary format. This file will usually have the app's name. To dump the headers from the file, use the class dump:

./class-dump Jailbreaking \ Bad

and you will be presented with a lot of output, but the part we’re interested in is this one:

@interface JBChecker : NSObject { } + (_Bool)isJailbroken; @end

What we can see here is the header of our checker class (the wrapper we added as described before), with the method name from which we can easily infer the usage and the return value we want to override. Gotcha.

4. A tweak that disables jailbreak detection

In a jailbreak world, a tweak is an improvement that allows you to hook into existing classes and methods of installed third-party and system apps. Tweaks are hosted on repositories or repos usually handled by Cydia. Default Cydia repos are community maintained and imply a certain level of trust and safety.

For the sake of this blog post, I’ve made a repo of my own:

GitHub - Adis

I won’t go into details on how to develop a tweak or host it on a repo since there’s a lot of work involved there, but my tool of choice for tweak development was Theos.

Back to work. Now that you know which method is responsible for making jailbreak checks, you'll want to write a tweak that will override the behavior of that method. This is just a bit more of programming. Here's the entire content of my own tweak:

// Class used to check for jailbreaks %hook JBChecker // Redefine the behavior of the function that checks // for jailbreaks + (BOOL)isJailbroken { return NO; } %end

The syntax is simple - %hook %end block injects itself into a class that checks for the jailbreak, and I rewrote the method that checks for jailbreaks to simply return false in all cases and skip any other checks.

In order to get this tweak on your own device, you'll need to add a new source in Cydia. The app will warn you that this is not a trusted repo, and that’s fine. As mentioned above, only a selected few are trusted.

When the repo is there, you'll find a single tweak called JBBad . After you install the tweak, go back and open the Jailbreaking Bad app. The app will - regardless of device status - report that the device is not jailbroken until you uninstall the tweak.

And a handy .gif with the boring parts sped up:

Not a lack of security in iOS apps

Although the process above may seem simple, it by no means shows the lack of security of iOS apps. Developing secure apps is a process more complex than having a simple jailbreak check. Devising a real security threat will require copious amounts of work.

The purpose of the article is to demonstrate that, at best, jailbreak detections are a tiny security upgrade. Such detections might only deter a part of your user base which is most likely well aware of the security issues a jailbreak presents.

Breaking bad-ass cover illustration is the work of designer Dubravko Tuksar.