writeup

InsecuriOS Labs - Jailbreak Detection (Objc Implementation)

This is a challenge about Jailbreak detection.

#ios

Initial Analysis

Challenge uses the isJailbrokenWithCompletion: method in JailbreakObjcChecker:

The verification logic performs multiple checks and combines results:

What it does:

Calls multiple jailbreak detection methods:
  • checkURLSchemes: Checks for Cydia/Sileo URL schemes
  • checkSuspiciousFiles: Checks for jailbreak-related files
  • checkWritableDirectories: Checks if system directories are writable
  • checkSymbolicLinks: Checks for suspicious symbolic links
  • checkOpenSystemFiles: Checks if system files can be opened
  • checkForJailbreakTweaks: Checks for loaded jailbreak tweaks
Combines all results using OR operations into w8 register
If any check returns 1, the final result is 1 (jailbroken)
Passes the boolean result to completion callback

LLDB Solution (Step by Step)

1. Start debugserver on device

# ssh into your device and run:
./debugserver 0.0.0.0:1337 -w "InsecuriOS Labs"

# on your mac terminal connect LLDB:
lldb
(lldb) process connect connect://<iPhone_IP>:1337
# example: process connect connect://192.168.1.100:1337

2. Find the jailbreak detection method

Using LLDB image lookup

(lldb) image lookup -r -n isJail

# look for JailbreakObjcChecker.isJailbrokenWithCompletion
# example output:
# Address: InsecuriOS Labs[0x0000000102778a08] (InsecuriOS Labs.__TEXT.__text + XXXXX)
# Summary: InsecuriOS Labs`+[JailbreakObjcChecker isJailbrokenWithCompletion:]

Important: Note the address in brackets [0x0000000102778a08], this is the file offset.

3. Calculate ASLR and set breakpoint

ASLR (Address Space Layout Randomization) means the binary is loaded at a random address each time. The runtime address needs to be calculated.

# step 1: get the ASLR slide
(lldb) p/x (uintptr_t)&_mh_execute_header - 0x100000000
# example output: (uintptr_t) 0x0000000000594000

# step 2: calculate runtime address (file_offset + ASLR slide = runtime_address)
# example: 0x0000000102778a08 + 0x0000000000594000 = 0x0000000102d0ca08
#          ^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^
#              file_offset        ASLR slide
(lldb) p/x 0x0000000102778a08 + 0x0000000000594000
# Output: (long) 0x0000000102d0ca08

# step 3: set breakpoint at the calculated address
(lldb) br s -a 0x0000000102d0ca08
# example output:
# Breakpoint 1: where = InsecuriOS Labs`+[JailbreakObjcChecker isJailbrokenWithCompletion:], address = 0x0000000102d0ca08

The breakpoint summary should show isJailbrokenWithCompletion: method.

4. Inspect the method

Now it's necessary to understand what this method does.

Continue app execution

(lldb) c

Go to the app and trigger the jailbreak check.

The debugger will stop at isJailbrokenWithCompletion:. Now inspect it:

(lldb) disas

The output will show assembly code. Look for:

0x102778a34 <+44>:  bl     0x10288c300    ; objc_msgSend$checkURLSchemes
0x102778a40 <+56>:  bl     0x10288c2c0    ; objc_msgSend$checkSuspiciousFiles
0x102778a4c <+68>:  bl     0x10288c320    ; objc_msgSend$checkWritableDirectories
0x102778a58 <+80>:  bl     0x10288c2e0    ; objc_msgSend$checkSymbolicLinks
0x102778a64 <+92>:  bl     0x10288c2a0    ; objc_msgSend$checkOpenSystemFiles
0x102778a70 <+104>: bl     0x10288c280    ; objc_msgSend$checkForJailbreakTweaks

These are the 6 jailbreak detection checks being called.

After the checks, the results are combined:

0x102778a74 <+108>: orr    w8, w0, w25    ; w8 = tweaks OR openSystemFiles
0x102778a78 <+112>: orr    w9, w24, w23   ; w9 = symlinks OR writableDirs
0x102778a7c <+116>: orr    w8, w8, w9     ; w8 = w8 OR w9
0x102778a80 <+120>: orr    w9, w22, w21   ; w9 = suspiciousFiles OR urlSchemes
0x102778a84 <+124>: orr    w8, w8, w9     ; w8 = all checks combined
0x102778a8c <+132>: and    w1, w8, #0x1   ; w1 = w8 & 1 (boolean for callback)

The and w1, w8, #0x1 instruction is the key point - this is where the final boolean is prepared for the completion callback.

5. Set breakpoint before the callback

# set breakpoint at the 'and' instruction (before callback)
(lldb) br s -a 0x102778a8c
# example output:
# Breakpoint 2: address = 0x102778a8c

6. Add commands to breakpoint

# add commands to automatically bypass the check
(lldb) breakpoint command add 2
# enter the following commands:
register write w8 0
continue
DONE

Explanation:

  • register write w8 0: Sets w8 to 0, so and w1, w8, #0x1 results in w1 = 0 (not jailbroken)
  • continue: Continues execution
  • DONE: Finishes command entry

Why w8?

Each check returns its result in x0, which gets saved to separate registers (x21-x25). After all checks complete, the results are combined using OR operations into w8. If any check detected jailbreak (returned 1), w8 will be 1.

By zeroing w8 before the and instruction, we ensure w1 = 0, and the completion callback receives false (not jailbroken).

7. Continue execution and trigger the bypass

(lldb) c

Now go to the app and trigger the jailbreak check again.

What will happen:

  1. Breakpoint 1 (isJailbrokenWithCompletion:) will be hit - type c to continue
  2. Breakpoint 2 (before callback) will be hit
  3. The automated commands will execute (register write w8 0 + continue)
  4. The completion callback receives false

8. Result

The app will continue executing and the jailbreak check will return false (not jailbroken).

Understanding the Assembly Flow

Register usage during checks

Check MethodReturn RegisterSaved To
checkURLSchemesx0x21 (w21)
checkSuspiciousFilesx0x22 (w22)
checkWritableDirectoriesx0x23 (w23)
checkSymbolicLinksx0x24 (w24)
checkOpenSystemFilesx0x25 (w25)
checkForJailbreakTweaksx0stays in w0

OR operations flow

w8 = w0 OR w25        (tweaks OR openSystemFiles)
w9 = w24 OR w23       (symlinks OR writableDirs)
w8 = w8 OR w9         (combine)
w9 = w22 OR w21       (suspiciousFiles OR urlSchemes)
w8 = w8 OR w9         (final result: all checks combined)

Key insight

The and wX, wY, #0x1 pattern at the end of boolean checks is a common target for bypasses. It's the "funnel" where all verification logic converges before returning/calling callback.

LLDB Commands Reference

Short commands used

Short commandFull CommandDescription
b <name>breakpoint set -n <name>Set a breakpoint on function name
br s -a <addr>breakpoint set -a <addr>Set a breakpoint at memory address
br com a <num>breakpoint command add <num>Add automated commands to breakpoint
ccontinueContinue program execution
disasdisassembleDisassemble current function
p/x <expr>print/x <expr>Print expression in hexadecimal
re read <reg>register read <reg>Read specific register values
re write <reg> <val>register write <reg> <val>Write value to register

Other commands

CommandDescription
image lookup -r -n <pattern>Search for symbols by regex pattern
breakpoint listList all breakpoints
breakpoint delete <num>Delete specific breakpoint