MHL 📵 No Escape 📵 Solution
I finally had time to work on one of the iOS mobile hacking exercises at Mobile Hacking Labs. I wanted to do a quick write-up on how I analyzed the No Escape
application using r2frida.
Analysis
This section will be a snapshot of the commands and outputs. Which in and of itself could be a standalone script for quick recon and intelligence gathering of an application.
Load into r2frida
❯ r2 'frida://spawn/usb//com.mobilehackinglab.No-Escape'
Check classes
[0x100e04868]> :ic~+escape
_TtC9No_EscapeP33_46A0F3C550DABD6C4FDD9346E5310C1E19ResourceBundleClass
EFSQLLikeEscapedExpression
No_Escape.SceneDelegate
No_Escape.ViewController
No_Escape.AppDelegate
Check methods
[0x100e04868]> :ic No_Escape.SceneDelegate
0x0000000100e05738 - window
0x0000000100e057e4 - setWindow:
0x0000000100e05af0 - scene:willConnectToSession:options:
0x0000000100e05b90 - sceneDidDisconnect:
0x0000000100e05c00 - sceneDidBecomeActive:
0x0000000100e05c70 - sceneWillResignActive:
0x0000000100e05ce0 - sceneWillEnterForeground:
0x0000000100e05e94 - sceneDidEnterBackground:
0x0000000100e05fd0 - init
0x0000000100e06038 - .cxx_destruct
[0x100e04868]> :ic No_Escape.ViewController
0x0000000100e00008 - messageLabel
0x0000000100e000b4 - setMessageLabel:
0x0000000100e003d8 - image
0x0000000100e00484 - setImage:
0x0000000100e01f20 - viewDidLoad
0x0000000100e021ac - initWithNibName:bundle:
0x0000000100e023d8 - initWithCoder:
0x0000000100e02458 - .cxx_destruct
[0x100e04868]> :ic No_Escape.AppDelegate
0x0000000100e02654 - window
0x0000000100e02700 - setWindow:
0x0000000100e029c8 - application:didFinishLaunchingWithOptions:
0x0000000100e03770 - application:configurationForConnectingSceneSession:options:
0x0000000100e03824 - application:didDiscardSceneSessions:
0x0000000100e0469c - init
0x0000000100e047d0 - .cxx_destruct
String search
[0x100e04868]> :/ Jail
Searching 4 bytes: 4a 61 69 6c
Searching 4 bytes in [0x0000000100dfc000-0x0000000100f60000]
hits: 1
0x100f4b5a0 hit0_0 Jail broken device!
[0x100e04868]> :/ jail
Searching 4 bytes: 6a 61 69 6c
Searching 4 bytes in [0x0000000100dfc000-0x0000000100f60000]
hits: 2
0x100f4b6cf hit1_0 jailbroken. This may compromise security. Quitting...
0x100f4c649 hit1_1 jailbreak_test.txt
[0x100f4c649]> :/ bin
Searching 3 bytes: 62 69 6e
Searching 3 bytes in [0x0000000100708000-0x000000010086c000]
hits: 5
0x1008586c0 hit1_0 bin/bash
0x1008586cf hit1_1 bin/sshd
0x1008586e2 hit1_2 bin
0x1008623c5 hit1_3 bined
0x100862515 hit1_4 bined
[0x102bd06cf]> :/ sbin
Searching 4 bytes: 73 62 69 6e
Searching 4 bytes in [0x0000000100698000-0x00000001007fc000]
hits: 1
0x1007e86ce hit1_0 sbin/sshd
List exports grep escape keyword
[0x104068868]> :iE~+escape
[TRUNCATED]
0x1041b8160 v $s9No_Escape11AppDelegateCMn
0x1041cf418 v $s9No_Escape11AppDelegateCN
0x104068788 f $s9No_Escape11AppDelegateCfD
0x10406a068 f $s9No_Escape12isJailbrokenSbyF
0x10406b3e0 f $s9No_Escape13ColorResourceV23__derived_struct_equalsySbAC_ACtFZ
0x10406b230 f $s9No_Escape13ColorResourceV4hash4intoys6HasherVz_tF
[TRUNCATED]
No_Escape12isJailbroken
Now that I have gone through my initial recon of the application I would like to overview the exported function responsible for the jailbreak check.
Parse functions of interest
[0x100690f70]> :iE~+jail
0x10054e068 f $s9No_Escape12isJailbrokenSbyF
Seek to the addresses and print out assembly instructions at that location. I was particularly interested in the tbz and mov instructions.
[0x104068868]> s 0x10406a068
[0x10406a068]> pd
[TRUNCATED]
0x10406a074 29000094 bl sym._s9No_Escape22checkForJailbreakFiles33_BCE8F13474E5A52C60853EA803F80A81LLSbyF
┌─< 0x10406a078 a0000036 tbz w0, 0, 0x10406a08c
┌──< 0x10406a07c 01000014 b 0x10406a080
└──> 0x10406a080 20008052 mov w0, 1
│ 0x10406a084 a0c31fb8 stur w0, [x29, -4]
┌──< 0x10406a088 04000014 b 0x10406a098
│└─> 0x10406a08c dc000094 bl sym._s9No_Escape33checkForWritableSystemDirectories33_BCE8F13474E5A52C60853EA803F80A81LLSbyF
│ 0x10406a090 a0c31fb8 stur w0, [x29, -4]
│┌─< 0x10406a094 01000014 b 0x10406a098
└└─> 0x10406a098 a8c35fb8 ldur w8, [x29, -4]
┌─< 0x10406a09c a8000036 tbz w8, 0, 0x10406a0b0
┌──< 0x10406a0a0 01000014 b 0x10406a0a4
└──> 0x10406a0a4 20008052 mov w0, 1
│ 0x10406a0a8 e00b00b9 str w0, [sp, 8]
┌──< 0x10406a0ac 04000014 b 0x10406a0bc
│└─> 0x10406a0b0 93010094 bl sym._s9No_Escape12canOpenCydia33_BCE8F13474E5A52C60853EA803F80A81LLSbyF
│ 0x10406a0b4 e00b00b9 str w0, [sp, 8]
│┌─< 0x10406a0b8 01000014 b 0x10406a0bc
└└─> 0x10406a0bc e80b40b9 ldr w8, [sp, 8] ; 5
┌─< 0x10406a0c0 a8000036 tbz w8, 0, 0x10406a0d4
┌──< 0x10406a0c4 01000014 b 0x10406a0c8
└──> 0x10406a0c8 20008052 mov w0, 1
│ 0x10406a0cc e00700b9 str w0, [sp, 4]
┌──< 0x10406a0d0 04000014 b 0x10406a0e0
│└─> 0x10406a0d4 1b020094 bl sym._s9No_Escape21checkSandboxViolation33_BCE8F13474E5A52C60853EA803F80A81LLSbyF
[TRUNCATED]
4 exported functions appear to be running on the device. Checking for a jailbroken device by looking for writeable system locations not present on a jailed device, binaries typically found on a jailed device, installation of cydia, and sandboxing violations.
Local Analysis of Binary
Since I found the exported function of interest I decided to also pull the IPA and analyze the ARM64 binary using radare2 on my laptop. This would give me additional insight into each function called via the No Escape
application.
Loading the binary into r2
❯ r2 -AA No\ Escape
Seek to function and locate 4 references to checks on the device.
[0x10000a068]> pdf~+sym
; CALL XREF from sym.func.1000047a0 @ 0x100004934(x)
; CALL XREF from sym.func.100006940 @ 0x10000696c(x)
┌ 176: sym.No_Escape.isJailbroken (int64_t arg_20h);
│ 0x10000a074 29000094 bl sym No_Escape.checkForJailbreakFiles._BCE8F13474E5A52C60853EA803F80A81 ; sym.No_Escape.checkForJailbreakFiles._BCE8F13474E5A52C60853EA803F80A81
│ │└─> 0x10000a08c dc000094 bl sym No_Escape.checkForWritableSystemDirectories._BCE8F13474E5A52C60853EA803F80A81 ; sym.No_Escape.checkForWritableSystemDirectories._BCE8F13474E5A52C60853EA803F80A81
│ │└─> 0x10000a0b0 93010094 bl sym No_Escape.canOpenCydia._BCE8F13474E5A52C60853EA803F80A81 ; sym.No_Escape.canOpenCydia._BCE8F13474E5A52C60853EA803F80A81
│ │└─> 0x10000a0d4 1b020094 bl sym No_Escape.checkSandboxViolation._BCE8F13474E5A52C60853EA803F80A81 ; sym.No_Escape.checkSandboxViolation._BCE8F13474E5A52C60853EA803F80A81
Seeking to each check addresses and analyze the add op code
Check for jailbreak files
│ 0x10000a168 00c01991 add x0, x0, 0x670 ; 0x100150670 ; "/Applications/Cydia.app"
│ 0x10000a198 00401a91 add x0, x0, 0x690 ; 0x100150690 ; "/Library/MobileSubstrate/MobileSubstrate.dylib"
│ 0x10000a1c0 00fc1a91 add x0, x0, 0x6bf ; 0x1001506bf ; "/bin/bash"
│ 0x10000a1e8 00241b91 add x0, x0, 0x6c9 ; 0x1001506c9 ; "/usr/sbin/sshd"
│ 0x10000a210 00601b91 add x0, x0, 0x6d8 ; 0x1001506d8 ; "/etc/apt"
│ 0x10000a238 00841b91 add x0, x0, 0x6e1 ; 0x1001506e1 ; "/bin"
Check for writable system directories
│ 0x10000a47c 00001991 add x0, x0, 0x640 ; 0x100150640 ; "/private/jailbreak_test.txt"
│ 0x10000a4b0 00701991 add x0, x0, 0x65c ; 0x10015065c ; "This is a test."
Check for cydia
pdf~add
│ 0x10000a7d4 00401891 add x0, x0, 0x610 ; 0x100150610 ; "cydia://package/com.example.package"
Check sandbox violation
│ 0x10000a958 00c01791 add x0, x0, 0x5f0 ; 0x1001505f0 ; "/private/var/lib/apt/"
Check for all tbz and mov op codes
[0x10000a068]> pdf~tbz; pdf~mov
│ ┌─< 0x10000a078 a0000036 tbz w0, 0, 0x10000a08c
│ ┌─< 0x10000a09c a8000036 tbz w8, 0, 0x10000a0b0
│ ┌─< 0x10000a0c0 a8000036 tbz w8, 0, 0x10000a0d4
│ ┌─< 0x10000a0e4 a8000036 tbz w8, 0, 0x10000a0f8
│ └──> 0x10000a080 20008052 mov w0, 1
│ └──> 0x10000a0a4 20008052 mov w0, 1
│ └──> 0x10000a0c8 20008052 mov w0, 1
│ └──> 0x10000a0ec 28008052 mov w8, 1
│ │└─> 0x10000a0f8 08008052 mov w8, 0
Approach 0x01
Approach one was to bypass the jailbreak detection using r2frida.
Begin trace of function.
[0x10054e068]> :dtf `:iE~+jail[0]`
true
[0x10054e068]> :dc
INFO: resumed spawned process
[0x10054e068]> [dtf onLeave][Mon Jul 01 2024 10:51:29 GMT-0700] 0x10292a068@0x10292a068 - args: . Retval: 0x1
[dtf onLeave][Mon Jul 01 2024 10:51:29 GMT-0700] 0x10292a068@0x10292a068 - args: . Retval: 0x1
Bypass boolean function
[0x10054e068]> :di0 `:iE~+jail[0]`
[0x10054e068]> :dc
Approach 0x02
I thought using an r2pipe script to automated this bypass would be a fun exercise and extend on some other posts I have done over the last few months. But I wanted to take a different approach for the bypass. Instead of bypassing the exact function itself and modifying the underlying value, what about overwriting the tbz and mov instructions in the function responsible for this check?
mov instructions
We are only concerned about the first 3 mov instructions in the exported function. So we will use the following one-liner to seek the location and print out all the mov instruction registers.
s `:iE~+jail[0]`; pd~mov[1]]
tbz instructions
We are only concerned about the 4 tbz instructions in the exported function. So we will use the one-liner below to seek to the location of the function and print out all the tbz instruction registers.
s `:iE~+jail[0]`; pd~tbz[1]
The final script can be found here. Thanks for reading. 😈