Application security

IOS Application Security Part 24 – Jailbreak Detection and Evasion

November 27, 2013 by Prateek Gianchandani

In this article, we will look at the checks a developer can incorporate in his application to check whether the device on which the application is running is jailbroken or not. Checking whether a device is jailbroken or not can have many advantages for your application. As we have already seen, an attacker can run tools like Cycript, GDB, Snoop-it etc to perform runtime analysis and steal sensitive data from within your application. If you are really looking to add an extra layer of security for your application, you should not allow your application to be run on a jailbroken device. Please note that millions of users jailbreak their devices and hence not allowing an application to be run on a jailbroken device could have a significant impact on your user base. Another thing you can do is instead block some of the features in your application rather than disabing it entirely. We will also look at how hackers can bypass the check for jailbreak detection in your application using Cycript.

Once a device is jailbroken, a lot of other files and applications are installed on the devcice. Checking for these files in the filesystem can help us identify whether the device is jailbroken or not. For e.g, most of the jailbreak softwares install Cydia on the device after jailbreaking. Hence just a simple check for the file path of Cydia can determine whether the device is jailbroken or not.

[c]
NSString *filePath = @”/Applications/Cydia.app”;
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
//Device is jailbroken
}
[/c]

However, not all devices that are jailbreaked have Cydia installed on them. In fact, most hackers can just change the location of the Cydia App. Checking for many other files related to Jailbroken devices can make this method much more efficient. For e.g, one can check if Mobile Substrate is installed on the device or not, which many applications require to run on a jailbroken device. One can also check for the location of the SSH Daemon, or the shell interpreter. Combining all these checks, we get a method like this.

[c]
+(BOOL)isJailbroken{

if ([[NSFileManager defaultManager] fileExistsAtPath:@”/Applications/Cydia.app”]){
return YES;
}else if([[NSFileManager defaultManager] fileExistsAtPath:@”/Library/MobileSubstrate/MobileSubstrate.dylib”]){
return YES;
}else if([[NSFileManager defaultManager] fileExistsAtPath:@”/bin/bash”]){
return YES;
}else if([[NSFileManager defaultManager] fileExistsAtPath:@”/usr/sbin/sshd”]){
return YES;
}else if([[NSFileManager defaultManager] fileExistsAtPath:@”/etc/apt”]){
return YES;
}
return NO;
}
[/c]

We have also learnt from the previous articles that applications that run as a mobile user run in a sandboxed environment and go inside the directory /var/mobile/Applications whereas applications that run with the root user (e.g Apple’s preloaded applications) aren’t subject to any sandbox environment and go inside the directory /Applications. A user running a jailbroken device can install your application in the /Applications folder thereby giving it root privileges. Hence, adding a check to see whether the application follows sandboxing rules can help the user identify whether the application is jailbroken or not. A good way to check for it would be to see if we can modify a file in some other location outside the application bundle.

[c]
NSError *error;
NSString *stringToBeWritten = @”This is a test.”;
[stringToBeWritten writeToFile:@”/private/jailbreak.txt” atomically:YES
encoding:NSUTF8StringEncoding error:&error];
if(error==nil){
//Device is jailbroken
return YES;
} else {
//Device is not jailbroken
[[NSFileManager defaultManager] removeItemAtPath:@”/private/jailbreak.txt” error:nil];
}
[/c]

We know that a skilled hacker can just modify the location of the application. However, we know that 80% or more of the devices that are jailbroken have Cydia installed on them, and even if the hacker can change the location of the Cydia app, he most probably won’t change the URL scheme with which the Cydia app is registered. If calling the Cydia’s URL scheme (cydia://) from your application gives a success, you can be sure that the device is jailbroken.

[c]
if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@”cydia://package/com.example.package”]]){
//Device is jailbroken
}
[/c]

Let’s also add a condition to make sure this code does not execute if we are testing our application on a simulator and not an actual device. After combining all the above techniques, our method looks like this.

[c]
+(BOOL)isJailbroken{

#if !(TARGET_IPHONE_SIMULATOR)

if ([[NSFileManager defaultManager] fileExistsAtPath:@”/Applications/Cydia.app”]){
return YES;
}else if([[NSFileManager defaultManager] fileExistsAtPath:@”/Library/MobileSubstrate/MobileSubstrate.dylib”]){
return YES;
}else if([[NSFileManager defaultManager] fileExistsAtPath:@”/bin/bash”]){
return YES;
}else if([[NSFileManager defaultManager] fileExistsAtPath:@”/usr/sbin/sshd”]){
return YES;
}else if([[NSFileManager defaultManager] fileExistsAtPath:@”/etc/apt”]){
return YES;
}

NSError *error;
NSString *stringToBeWritten = @”This is a test.”;
[stringToBeWritten writeToFile:@”/private/jailbreak.txt” atomically:YES
encoding:NSUTF8StringEncoding error:&error];
if(error==nil){
//Device is jailbroken
return YES;
} else {
[[NSFileManager defaultManager] removeItemAtPath:@”/private/jailbreak.txt” error:nil];
}

if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@”cydia://package/com.example.package”]]){
//Device is jailbroken
return YES;
}
#endif

//All checks have failed. Most probably, the device is not jailbroken
return NO;
}
[/c]

Honestly speaking, there is no foolproof method of detecting jailbroken devices. A skilled hacker will always find a way to bypass these checks. He can simply find the instructions in the binary and replace all instructions with No-op. He can also swizzle your method implementation with his own using Cycript.

He can first find the class information of the application using Class-dump-z. Over here, he can see a method named +(BOOL)isJailbroken in the JailbreakDetector class. Note that it is a class method as it begins with positive sign. It obviously means this method checks whether a device is jailbroken or not and returns YES if the device is jailbroken. If you are not getting any of this, you should consider reading previous articles.


He can then hook into this application using Cycript.


And then print out all the methods for the JailbreakDetector class. Please note that we are using JailbreakDetector->isa.messages because isJailbroken is a class method. To find the instance methods, just using JailbreakDetector.messages would have worked for us.


And then he can swizzle the method implementation with his own that always returns a NO. If you are not getting this, i suggest that you read the article on Method Swizzling.

As a developer, what we can do is change the method name to something that doesn’t look quite appealing to the hacker. For e.g, the className JailbreakDetector could be renamed as ColorAdditions and the method +(BOOL)isJailbroken could be replaced by +(BOOL)didChangeColor with the implementation being the same. Something like this wouldn’t attract the attention of the hacker. He can always look at the calls that are being made inside this method using Snoop-it, GDB etc, but a small change like this can surely help in confusing him.

Posted: November 27, 2013
Author
Prateek Gianchandani
View Profile

Prateek Gianchandani, a recent IIT graduate, has interests in the field of Penetration Testing, Web Application Security and Intrusion Detection. He is currently a researcher for InfoSec Institute. In the past he has worked for security-based startups. You can contact him at prateek.searchingeye@gmail.com and on twitter @prateekg147 or you can visit his personal website at highaltitudehacks.com

6 responses to “IOS Application Security Part 24 – Jailbreak Detection and Evasion”

  1. Gorkem says:

    Nice explanation thanks

  2. delauret says:

    In regards to submitting apps to the app store with this code:
    The App Store Review Guidelines state that “Apps that read or write data outside its designated container area will be rejected”.

    Do Apple see this code as not adhering to their guidelines or do they see this as a good prevention measure?

  3. Really good article.

    Just 2 additions:
    1. When you are writing a string into the jailbreak.txt file, you have a typo in your error handling. Remove the “&” inside “…encoding:NSUTF8StringEncoding error:&error];” It should be “…encoding:NSUTF8StringEncoding error:&error];”
    2. In your if…else block when you are checking for an error upon writing jailbreak.txt into the file system, your else block makes no sense. You need to remove the file if there was NO error. Right now you are removing the file if there was an error.

    What do you think?

    Cheers
    MS II.

  4. Chadbag says:

    Jailbreak and antic rack calls should not be Objective-C but straight C (or should exist and be used in both C and Objective-C forms). This will make it harder to do Objective-C method swizzling and class-dump etc. Also try and use standard C library calls in addition to the NS Frameworks for things like file checks. Doing them twice using both methods makes it harder to stamp out. Not perfect but makes things a bit harder.

  5. funnycash says:

    Many thank for your article !

  6. Z.Z. says:

    still useful.Even today. thanks

Leave a Reply

Your email address will not be published.