Bypassing jailbreak detection mechanisms
In this post, I will talk about the challenges that this week with the mechanisms of Jailbreak Detection
and how to bypass it.
In much important software such as banking software, mechanisms such as Jailbreak Detection or debugging are incorporated to prevent software implementation.
The use of these types of mechanisms can have various reasons, such as preventing software lock bypassing by using debugging and dumping sensitive software information, etc. For example, if you can bypass the lock screen of the software by debugging the software, you can access the information stored in it.
In the scenario that I have been dealing with all this time, I have to bypass the jailbreak detection mechanisms of an IOS software so that we can do various things such as traffic capture etc.
Dynamic analysis
First, I captured a list of all function calls using frida-trace
|
|
At the very beginning of the list, I came across an item that caught my attention
|
|
With a little search, I found out that the IOSSecuritySuite library was used, as it is written in the description of this project, it is a library to prevent anti-tampering on the IOS platform.
This library has different parts that are explained in the table below each of them
Class | Description |
---|---|
DebuggerChecker | This class has methods to check the status of the software, which determines whether the software is in debugging mode or not |
EmulatorChecker | This class has methods to check the execution of the software in the Emulator environment |
JailbreakChecker | This class has methods to check read and write access to paths that only root has access to |
ReverseEngineeringToolsChecker | This class has methods to check for the presence of reverse engineering tools |
Using this script I collected all the classes related to IOSSecuritySuite:
|
|
|
|
At first, I looked for scripts ready to bypass IOSSecuritySuite, one of these scripts was Darkprince-Jailbreak-Detection-Bypass, but this script only bypasses the JailbreakChecker class.
I decided to hook
IOSSecuritySuite methods so that the inputs and outputs of each can be manipulated. For example, if the amIDbugged
method returns true
, I can manually change it to false
, for example:
|
|
There is a repository with the topic “Jailbreak/Root Detection Bypass in Flutter” which I followed, but then I faced a serious challenge.
I could not find almost any of the functions like amIDebugge
with its original name, even the names with formats like $s16IOSSecuritySuiteAAC13amIJailbrokenSbyFZ
did not exist. They probably created an obfuscation in the code so that we could not easily find the functions.
The names of some functions were in the following format: IOSSecuritySuiteXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXFishHookXXXX
But I did not find all the functions :)
Static analysis
Next, I decided to decompile the project, the ipa files consist of different parts:
|
|
I decided to look for IOSSecuritySuite classes to patch any spots that used these classes.
I started the search from Constant values like strings, for example, in the DebuggerChecker
class there is a string with this value "Error occurred when calling sysctl(). The debugger check may not be reliable"
This can be a good thread to find the DebuggerChecker
class be
Original source code:
|
|
The same part is decompiled:
Other classes can be found in the same way:
|
|
JailbreakChecker class
Almost all classes can be found in this way, and we can debug it to check the correctness of this search.
Debugging
There are different ways to debug iOS software:
We need Debugserver to start the debug environment.
debugserver is a console app that acts as server for remote gdb or lldb debugging. It is installed when a device is marked for development. It can be found in /Developer/usr/bin/debugserver. This is also the process invoked by Xcode to debug applications on the device.
Setup Debugserver
|
|
If you do not have access to xcode, you can download DeveloperDiskImage from this repository:
|
|
options can be as follows:
Option | Effect |
---|---|
-a process | Attach debugserver to process. The process can be a pid or executable name. |
-d integer | Assign the waitfor-duration. |
-f ? | ? |
-g | Turn on debugging. |
-i integer | Assign the waitfor-interval. |
-l filename | Log to file. Set filename to stdout to log to standard output. |
-t | Use task ID instead of process ID. |
-v | Verbose. |
-w ? | ? |
-x method | |
–launch=method | How to launch the program. Can be one of: |
- auto: Auto-detect the best launch method to use. | |
- fork: Launch program using fork(2) and exec(3). | |
- posix: Launch program using posix_spawn(2). | |
- backboard: Launch program via BackBoard Services. | |
The backboard option is only available in the closed-source version included in Xcode. | |
–lockdown | Obtain parameters from lockdown (?) |
The vanilla debugserver lacks the task_for_pid() entitlement. For building and debugging your own apps on a properly provisioned device, this is not a problem; assuming your project and device are properly configured with your active iOS Developer Program, debugserver should have no trouble attaching to an app built and sent down to the device by Xcode. However, debugserver cannot attach to any other processes, including other apps from the App Store, due to lack of entitlement to allow task_for_pid(). An entitlement must be inserted into the binary to allow this. Note: The /Developer directory is actually a mounted read-only ramdisk. You cannot add any entitlements to the copy of debugserver installed there; it must be extracted to another directory and used from there.
Save the following xml as entitlements.xml:
|
|
Apply the entitlement with ldid:
|
|
ldid is a tool made by saurik for modifying a binary’s entitlements easily. ldid also generates SHA1 and SHA256 hashes for the binary signature, so the iPhone kernel executes the binary. The package name in Cydia is “Link Identity Editor”.
ldid -e
ldid -Sent.xml
ent.xml
is the path to an entitlements file.
ldid -S
pseudo-signs
a binary with no entitlements.
Attaching to a process
On the device, type:
|
|
To run an application in debug mode:
|
|
This will launch the app and wait for remote troubleshooting.
Debugging through USB instead of WiFi
After going through these steps, I found that debugging via WIFI is slow, an alternative solution is to debug via USB, for this you can use libimobiledevice.
Unfortunately, libimobiledevice is not compiled for Windows. Download the compiled version for Windows from this fork
iproxy - A proxy that binds local TCP ports to be forwarded to the specified ports on a usbmux device.
EXAMPLES
iproxy 2222:44
- Bind local TCP port
2222
and forward to port44
of the first device connected via USB.
- Bind local TCP port
SSH proxying:
|
|
debugserver proxying:
|
|
Configuring the debugger
To connect IDA to debugserver, follow the steps below:
Debugger
->Select debugger…
and select Remote iOS Debugger
:
Now go to Debugger
>Process options...
> and ensure the following fields are set:
- Hostname:
localhost
- Port:
1234
And Debugger
>Attach to process...
>
And finally it connects:
Next, I checked the classes I had found.
Patching
After checking the points where IOSSecuritySuite
classes are used, I came to the conclusion that it is enough to patch the points where IOSSecuritySuite functions are used.
For example, see the image below:
The sub_10303D914
function is basically one of the methods of IOSSecuritySuite whose output specifies the jailbreak status.
If we go to sub_10303D914
:
It is clear that this is the JailbreakChecker class.
|
|
We need to change code BL sub_10303D914
to NOP
to prevent W0
register value from changing.
|
|
Unfortunately, IDA does not support arm patching and you will be faced with the following message:
|
|
That’s why I used Keypatch.
Multi-architecture assembler for IDA Pro. Powered by Keystone Engine.
First, you need to add the Keypatch plugin to IDA
Then we click on the desired instruction and press Ctrl + Alt + K
keys
We change the value of the Assembly field to NOP
After patching the instructions will change like this:
|
|
After patching all parts, go to Edit -> Patch Program -> Patches to input file
and save the file.
Bypassing IOS Code Signatures
After patching the binary, IOS prevents the application from running because the application signature is invalid.
First, I tried to disable the signature verification function through sysctl
and changing the value of proc_enforce to 0
, but it seems that this method no longer works.
|
|
Next, I got acquainted with AppSync Unified
AppSync Unified is a tweak that allows users to freely install ad-hoc signed, fakesigned, or unsigned IPA app packages on their iOS devices that iOS would otherwise consider invalid.
Follow the steps below to install AppSync:
- Add cydia.akemi.ai to
Cydia
sources - Search for the
AppSync Unified
package and then install it
Using the ldid tool, we register a fakesign
for our binary:
|
|
Now we can run the program and that’s it
Links
- https://github.com/securing/IOSSecuritySuite
- https://iphonedev.wiki/Code_Signing
- https://codeshare.frida.re/@sridharas04/darkprince-jailbreak-detection-bypass/
- https://github.com/CyberCX-STA/flutter-jailbreak-root-detection-bypass
- Jailbreak/Root Detection Bypass in Flutter
- Debugging iOS Applications with IDA Pro
- The missing guide to debug third party apps on iOS 12+
- https://iphonedev.wiki/Debugserver
- https://github.com/pdso/DeveloperDiskImage
- https://iphonedev.wiki/Ldid
- https://github.com/libimobiledevice/libimobiledevice
- https://man.archlinux.org/man/extra/libusbmuxd/iproxy.1.en
- https://www.keystone-engine.org/keypatch/tutorial/