Patching iOS jailbreak detection
It seems that more and more applications in the Apple App Store are implementing functions to check the authenticity of the device/Operating System the app is running on, resulting in forbidden or restricted usage. Jailbroken devices come with many security risks, so it's no surprise that many companies such as banks, do not want user account info being compromised by a rogue keylogger. Another common use is companies trying to prevent tweaking or bypassing of application restrictions.
While I applaud developers for adding jailbreak/root detection logic, it can prove to be a hurdle for penetration testers or researchers that are interested in testing the overall functionality or security posture of that platform. There are multiple ways to bypass security protections in iOS apps, such as debuggers and other run-time manipulation tools like cycript or xCon. My issue with bypassing something in run-time is that you will likely have to repeat this process every time the app starts or the logic is called. That's a pain, which is why I prefer binary patching. Binary patching allows for a persistent bypass.
This write-up is for anyone that may be getting into mobile testing, and runs into this hurdle on a test where time is money. The following sections will walk through dumping the encrypted application, disassembling the executable, finding and following the pseudo code, patching strings, rebuilding, signing, and installing the new modified application. Sounds like a lot, but the steps are not terribly difficult.
iOS Applications are encrypted at rest, so we will need a tool like Clutch to decrypt the application, which will allow us to perform static analysis in a Disassembler. Clutch basically sets a breakpoint in the app loading process, dumps the application from memory, then fixes the load commands. The decrypted application is placed in a Dumped directory (/private/var/mobile/Documents/Dumped/..) as an IPA file(iOS Application Archive).
Assuming you're operating on a jailbroken iPhone, there are two options for installing Clutch. You can compile it from the KJCracks github (you need Xcode installed), or you can add the iPhoneCake repository to Cydia and install Clutch 2.0 from there.
SSH into your iPhone and run
clutch -i and list all of the installed applications. This will only list apps that have been installed via the App Store. They are listed by number, so simply run
clutch -d 1 to dump app 1. See images below:
Once you have dumped the decrypted app using Clutch, you can move the archive to your desktop with something like iFunBox or
scp. The IPA file is just an archive, so unzip and you will find a "Payload" folder. Inside that folder is the app bundle. Right click the app and choose "Show Package Contents". Inside you will find the unix executable. Copy the binary to the Desktop, leaving the original version in the directory. See images below:
If you're an IDA fan then by all means use IDA, but for iOS I love Hopper Disassembler. It's fairly easy to use, and cheap. In Hopper, select File -> Read Executable to Disassemble, and choose the binary we just copied to the Desktop. Click Ok and let Hopper load up the binary. Once Hopper is done, we can start looking around for strings or labels that might lead us to the function that performs the checks. For this unnamed app I'm using in this example, I searched for "jailbreak" and two string references were found.
On the labels tab, I chose the first result, which points to a constant containing a phrase warning the user about the dangers of running this app on a jailbroken device.
Right Click the address highlighted in the main window and select "References to ...".
This will open a window with all address references in it.
Let's check out the method at the top and select the Pseudo Code tab. A quick glance at the method shows two strings, "/bin/sh" and "MobileSubstrate" that tell us that the app might be searching for the presence of bash and the MobileSubstrate dylib which is installed along with Cydia during jailbreak. MobileSubstrate is a set of APIs that allow hooking of native or Objective-C funtions.
There are different procedures for detecting a jailbroken device, but the existence of binaries/directories seem to be faily easy to develop, which is probably why I see them more often. Trustwave posted a blog which contains a list of popular files that jailbreak checks look for, as well as other methods for determining a jailbroken device.
So if you know that these things are present because we are in fact operating on a jailbroken device, then let's change these strings so they always return false. (Another way would be to change the instruction doing the comparison (branch if equals, branch if not equals), thereby redirecting code flow to always pass the checks.)
In Hopper, to edit strings we need to edit the hex values. All we need to change is a few characters. In the image below, you can see I edited the strings by changing the hex values. The values changed are highlighted red.
Once the hex is edited, select File -> Produce New Executable. Save to the Desktop as AppName-modified.
Copy the modified binary into the "Show Package Contents" we looked at earlier. Then we can remove the original binary, and remove the "-modified" from the patched binary name.
Next we need to archive the app package. Highlight the two items in the unzipped package (iTunesArtwork & Payload folder), right click and select Compress 2 items. Rename the zip to a .ipa.
Using Cydia Impactor, we can sign the package and install on the conncected device. All applications must be signed by a developer license in order to be installed in iOS. Any Apple ID can sign an application, but the signature will expire after 7 days. An Apple Developer License is roughly $100 a year and this will keep you from having to resign a patch app every 7 days. Xcode tools can be used to sign and install applications, but Cydia Impactor works fine for me and is easy. Just drag the .ipa into Impactor, then it will prompt for the Apple ID to sign the app.
Run the app to check if jailbreak detection was successfully bypassed.
Screenshot was omitted to keep the example app Unnamed. ;)