Introduction

In this blog post we will discuss the use of Corellium emulator in Penetration Testing engagements. This blog post will also provide a walktrough on how to bypass Jailbroken detection using frida and r2frida, a plugin for radare2 that allows to instrument remote processes using frida.

Corellium, is a powerful platform that allows for the virtualization and emulation of iOS devices. It provides researchers, application developers, and security professionals the ability to analyze and test iOS applications in a controlled environment without physical access to an actual device. By running iOS instances within Corellium, it is easy to examine the behavior of mobile apps as well as the underlying system in a variety of Jailbroken and non-Jailbroken virtual devices.

First we will go over the Corellium emulator and we will examine the provided features regarding the connectivity with the virtual device as well as some general setup guides. Afterwards we will connect to the emulator in order to use the most common tools such as frida in order to show ways to bypass SSL pinning as well as to bypass Jailbreak detection controls in iOS applications.

We will not go through all the details of the registration, or how to buy the license etc., but we will examine the potentials of using such a platform when performing research or penetration testing activities. Regarding the connectivity to the Corellium iOS device, the .ovpn file must be downloaded locally and then run through VPN applications such as Tunnelblick.

The following screenshot shows the Corellium emulator and the provided features such as a button to download the .ovpn file. After we logged into the Corellium platform we setup an iPhone 8 virtual device which is jailbroken.

The setup of the virtual device is easy. We chose a Jailbroken device in order to perform tests to bypass the jailbreak detection control which is implemented in iOS apps as a safeguard to reduce the possibility of sensitive data leakage in Jailbroken devices.

Connecting to iOS virtual device using Corellium’s USBflux

Using Frida and Objection bundled with Corellium’s USBFlux allows to test applications as if the device was directly connected to the local machine. This is a flexible solution offered by Corellium as we can easily perform instrumentation to test applications on iOS devices.

In order to use the USBFlux we should first download it from github

~/ git clone https://github.com/corellium/usbfluxd.git

Afterwrds, on MacOS we should run the following command in order to install the necessary libraries and tools before we build the binary

~/ brew install make automake autoconf libtool pkg-config gcc libimobiledevice usbmuxd

Then we will build and install the binary as follows

~/ cd usbfluxd
~/usbfluxd/ [master] ./autogen.sh
~/usbfluxd/ [master] make
~/usbfluxd/ [master] sudo make install

Now we should be able to accept the USB connection over TCP sockets and appear as local.

~/usbfluxd/ [master] sudo launchctl start usbFluxd
~/usbfluxd/ [master] export PATH=/usr/local/sbin:${PATH}
~/usbfluxd/ [master] sudo usbfluxd -f -r 10.11.1.2 5000
[21:41:39.162][3] usbfluxd v1.2.1 starting up
[21:41:39.164][4] Original usbmuxd socket file renamed: /var/run/usbmuxd -> /var/run/usbmuxd.orig
[21:41:39.164][4] Creating socket
[21:41:39.165][4] New Remote fd 4
[21:41:39.165][3] Initialization complete
[21:41:39.444][1] 10.11.1.2:5000 is open
[21:41:39.444][4] New Remote fd 8
[21:41:39.444][3] remote_mux_service_add: new remote id: 1
[21:41:39.444][3] Added remote 10.11.1.2:5000
[21:41:39.587][1] [email protected].:5000 is open
[21:41:39.587][4] New Remote fd 9
[21:41:39.587][3] remote_mux_service_add: new remote id: 2
[21:41:39.587][3] service_browse_cb: Added service usbmuxd@10-11-1-2
[21:41:49.881][1] [email protected].:5000 is open
[21:41:49.881][1] 10.11.1.2:5000 is open
[…]

At this point we install the DVIAswiftv2 application on the iOS virtual device on Corellium at Apps tab pressing the install app button.

Now let’s start the DVIAswiftv2 application.

After running USBFlux we should be able to use frida tool and list the applications on the iOS virtual device.

 ~/ frida-ps -Uai
 PID   Name          Identifier
—————-  ——————————–
1224   DVIA-v2       com.highaltitudehacks.DVIAswiftv2

   –   App Store     com.apple.AppStore

   –   Books            com.apple.iBooks
  
   –   Calculator    com.apple.calculator
  
   –   Calendar      com.apple.mobilecal

[….]

As seen above when we run the command frida-ps -Uai we see the PID of the running application, the Name of the app and the Identifier. Also, the output from running frida tool shows us that the virtual USB connection is already enabled.

Installing r2frida plugin

Assuming that radare2 is already installed on the local machine, using the following commands we are also able to install r2frida plugin. This plugin aims to join the capabilities of static analysis of radare2 and the instrumentation provided by frida. The recommended way to install r2frida is by using r2pm

The following command will initialize the package control

~/ r2pm init

After the initialization the package manager will have the plugins ready to install. We will run the following command in order to install r2frida plugin

~/ r2pm -ci r2frida

 

[….]

pkg-config –cflags r_core

-I/usr/local/Cellar/radare2/5.8.8/include/libr

cc src/io_frida.o -o io_frida.dylib -fPIC -g -L/usr/local/Cellar/radare2/5.8.8/lib -lr_core -lr_config -ldl -lr_debug -ldl -lr_bin -ldl -lr_lang -ldl -lr_anal -ldl -lr_bp -ldl -lr_egg -ldl -lr_asm -ldl -lr_flag -ldl -lr_search -ldl -lr_syscall -ldl -lr_fs -ldl -lr_magic -ldl -lr_arch -ldl -lr_esil -ldl -lr_reg -ldl -lr_io -ldl -lr_socket -ldl -lr_cons -ldl -lr_crypto -ldl -lr_util -ldl -shared -fPIC -Wl,-exported_symbol,_radare_plugin -Wl,-no_compact_unwind ext/frida/libfrida-core.a -framework Foundation -lbsm -framework AppKit -lresolv

mkdir -p /”/Users/xenovas/.local/share/radare2/plugins”

mkdir -p /”/Users/xenovas/.local/share/radare2/prefix/bin”

rm -f “//Users/xenovas/.local/share/radare2/plugins/io_frida.dylib”

cp -f io_frida.dylib* /”/Users/xenovas/.local/share/radare2/plugins”

cp -f src/r2frida-compile /”/Users/xenovas/.local/share/radare2/prefix/bin”

The succesful execution of the command above will download the plugin from the specified repo and then after building and installing the plugin, the r2frida will be able to run

The following command shows the installed apps as well as the running apps on the virtual device

~/ r2 frida://apps/usb

PID           Name Identifier

—————————–

[…..]

– Podcasts             com.apple.podcasts

– Reminders         com.apple.reminders

– Safari                   com.apple.mobilesafari

– Settings               com.apple.Preferences

– Shortcuts           com.apple.shortcuts

– Stocks                 com.apple.stocks

– Substitute          com.ex.substitute.settings

– TV                        com.apple.tv

– Tips                     com.apple.tips

– Translate           com.apple.Translate

– Voice Memos    com.apple.VoiceMemos

– Wallet                 com.apple.Passbook

– Watch                 com.apple.Bridge

– Weather            com.apple.weather

– iTunes Store     com.apple.MobileStore

2754 DVIA-v2    com.highaltitudehacks.DVIAswiftv2

Since we are running the DVIA-v2 application, we are able to see the PID from the output above. At this point we are able to perform our analysis to the DVIA-v2 application. Lets start by attaching to the running DVIA-v2 application using the following command

[?] ~/ r2 frida://attach/usb//2754

INFO: Using safe io mode.

 

— Enable asm.trace to see the tracing information inside the disassembly

[0x00000000]>

From here we can search the available command using the help command : ?

[0x00000000]> : ?

r2frida commands are prefixed with `:` (alias for `=!`).

:. script                                               Run script

:frida-expression                             Run given expression inside the agent

:/[x][j] <string|hexpairs>                Search hex/string pattern in memory ranges (see search.in=?)

:/v[1248][j] value                              Search for a value honoring `e cfg.bigendian` of given width

:/w[j] string                                       Search wide string

:<space> code..                                Evaluate Cycript code

: ?                                                         Show this help

:?e message                                     Show message like ?e but from the agent

:?E title message                            Show UIAlert dialog with given title and message

:?V                                                       Show target Frida version

:chcon file                                         Change SELinux context (dl might require this)

:d.                                                        Start the chrome tools debugger

:dbn [addr|-addr]                           List set, or delete a breakpoint

:dbnc [addr] [command]              Associate an r2 command to an r2frida breakpoint

:db (<addr>|<sym>)                        List or place breakpoint (DEPRECATED)

:db- (<addr>|<sym>)|*                    Remove breakpoint(s) (DEPRECATED)

:dc                                                       Continue breakpoints or resume a spawned process

:dd[j-][fd] ([newfd])                         List, dup2 or close filedescriptors (ddj for JSON)

:di[0,1,-1,i,s,v] [addr]                          Intercepts and replace return value of address without calling the function

:dif[0,1,-1,i,s] [addr]                           Intercepts return value of address after calling the function

:dk ([pid]) [sig]                                  Send specific signal to specific pid in the remote system

:dkr                                                      Print the crash report (if the app has crashed)

:dl libname                                        Dlopen a library (Android see chcon)

:dl2 libname [main]                        Inject library using Frida’s >= 8.2 new API

:dlf path                                             Load a Framework Bundle (iOS) given its path

:dlf- path                                            Unload a Framework Bundle (iOS) given its path

:dm[.|j|*]                                             Show memory regions

:dma <size>                                       Allocate <size> bytes on the heap, address is returned

:dma- (<addr>…)                              Kill the allocations at <addr> (or all of them without param)

:dmad <addr> <size>                      Allocate <size> bytes on the heap, copy contents from <addr>

:dmal                                                  List live heap allocations created with dma[s]

:dmas <string>                                 Allocate a string initiated with <string> on the heap

:dmaw <string>                               Allocate a widechar string initiated with <string> on the heap

:dmh                                                   List all heap allocated chunks

:dmh*                                                 Export heap chunks and regions as r2 flags

:dmhj                                                  List all heap allocated chunks in JSON

:dmhm                                               Show which maps are used to allocate heap chunks

:dmm                                                 List all named squashed maps

:dmp <addr> <size> <perms>     Change page at <address> with <size>, protection <perms> (rwx)

:dp                                                      Show current pid

:dpt                                                    Show threads

:dr                                                       Show thread registers (see dpt)

:dt (<addr>|<sym>) ..                      Trace list of addresses or symbols

:dt- (<addr>|<sym>)                       Clear trace

:dt-*                                                    Clear all tracing

:dt.                                                      Trace at current offset

:dtf <addr> [fmt]                             Trace address with format (^ixzO) (see dtf?)

:dth (addr|sym)(x:0 y:1 ..)               Define function header (z=str,i=int,v=hex barray,s=barray)

:dtl[-*] [msg]                                     Debug trace log console, useful to .:T*

:dtr <addr> (<regs>…)                    Trace register values

:dts[*j] seconds                               Trace all threads for given seconds using the stalker

:dtsf[*j] [sym|addr]                         Trace address or symbol using the stalker (Frida >= 10.3.13)

:dxc [sym|addr] [args..]                  Call the target symbol with given args

:e[?] [a[=b]]                                       List/get/set config evaluable vars

:env [k[=v]]                                        Get/set environment variable

:eval code..                                        Evaluate Javascript code in agent side

:fd[*j] <address>                              Inverse symbol resolution

:i                                                          Show target information

:iE[*] <lib>                                         Same as is, but only for the export global ones

:iS[*]                                                    List sections

:iS.                                                       Show section name of current address

:iSj                                                       List sections in Json format

:iSS[*]                                                  List segments

:iSS.                                                     Show segment name of current address

:iSSj                                                     List segments in Json format

:ic <class>                                          List Objective-C/Android Java classes, or methods of <class>

:ii[*]                                                      List imports

:il                                                          List libraries

:ip <protocol>                                   List Objective-C protocols or methods of <protocol>

:is[*] <lib>                                           List symbols of lib (local and global ones)

:isa[*] (<lib>) <sym>                         Show address of symbol

:j java-expression                             Run given expression inside a Java.perform(function(){}) block

:r [r2cmd]                                           Run r2 command using r_core_cmd_str API call (use ‘dl libr2.so)

:t [swift-module-name]                  Show structs, enums, classes and protocols for a module (see swift: prefix)

[0x00000000]>

We can check about process information as follows

[0x00000000]> :i

 

arch                                      arm

bits                                       64

os                                          darwin

pid                                        2754

uid                                        501

objc                                      true

runtime                               QJS

swift                                     false

java                                      false

mainLoop                           true

pageSize                             16384

pointerSize                         8

modulename                    DVIA-v2

modulebase                      0x100fd4000

codeSigningPolicy           optional

isDebuggerAttached       false

cwd                                       /

bundle                                 com.highaltitudehacks.DVIAswiftv2

exename                             DVIA-v2

appname

appversion                         2.0

appnumversion                16809984

minOS                                 10.0

homedir                              /var/mobile/Containers/Data/Application/88D40FF5-D86E-4481-B344-A64B2D4107ED

tmpdir                                 /private/var/mobile/Containers/Data/Application/88D40FF5-D86E-4481-B344-A64B2D4107ED/tmp/

bundledir                            /private/var/containers/Bundle/Application/DA04525C-E14F-41ED-9C32-E2AE617E1CCE/DVIA-v2.app

[0x00000000]>

Jailbreak detection

Jailbreak detection is a safeguard mechanism by which it can be detected if an application is running on a Jailbroken device. It is crucial for applications to be able to detect if they are running in a Jailbroken device as the absent of this security mechanism could leave the application susceptible to security issues. Jailbreaking in iOS and Rooting in Android devices involves running a privilege escalation on the device. Also, the process of Jailbreaking gives the ability to alter or replace system applications, files, and settings, removing pre-installed applications, and running specialized applications that require administrator-level permissions. Furthermore, malicious agents, who previously targeted laptops and PCs, are refocusing on mobile devices because this presents the opportunity to successfully carry out malicious objectives.

Moreover to ensure security of corporate data, it is recommended that jailbroken devices must not be used in organizations. Mobile Device Managment (MDM) solutions allow organizations to detect jailbroken devices in the network and also remove these devices once they are detected. After detection these devices cannot be enrolled into Moble Device Managment (MDM) and thus lose access to corporate data.

Run-Time Jailbreak detection bypass using r2frida

Run-time jailbreak detection bypass enables the manipulation of a specific value at run time, but the drawback here is that this technique is not permanent. As an example we can use frida or objection for bypassing the jailbreak detection at run-time. Once the jailbreak detection is bypassed, it only works untill frida, objection or other instrumentation tool stops running.

At this point we will use r2frida in order to perform static analysis and find the specific class and method to bypass the Jailbreak detection mechanism.

First we need to filter out the class that is related with the Jailbreak functionality on the target DVIA-v2 application. We will use the :ic ~+jailbreak command in order to filter case insensitive class information

~/ r2 frida://attach/usb//2754
INFO: Using safe io mode.
 — Use /m to carve for known magic headers. speedup with search.
[0x00000000]> :ic ~+jailbreak
JailbreakDetection
DVIA_v2.JailbreakDetectionViewController
[0x00000000]>

As we see from the output above the JailbreakDetection class has been revealed. After that, we are now in a position of knowing what we should examine for a potential jailbreak bypass. At this point we will run the following command in order to retrieve information about the method used to check if the device is Jailbroken

[0x00000000]> :ic JailbreakDetection

0x00000001011412d8 + isJailbroken
[0x00000000]>

We can also confirm that the method isJailbroken belongs to JailbreakDetection class as follows

[0x00000000]> :eval ObjC.classes.JailbreakDetection.isJailbroken()

true
[0x00000000]>

In case we already know the name of the method we can search for it using the following command

[0x00000000]> :eval new ApiResolver(‘objc’).enumerateMatchesSync(‘*[* isJailbroken]’)

[{“name”:”-[PFDevice isJailbroken]”,”address”:”0x102224a9c”},{“name”:”+[JailbreakDetection isJailbroken]”,”address”:”0x1011412d8″}]
[0x00000000]>

As seen from the output above the address of the isJailbroken method is 0x1011412d8. We can trace this method by using the following command.

[0x00000000]> :dtf 0x00000001011412d8

true
[0x00000000]>

The :dtf command above returns true signaling that the command was issued succesfully. From the menu of the DVIA-v2 app go to Jailbreak detection and then choose Jailbreak Test 2.

Then after clicking the Jailbreak Test 2, from the trace command used before :dtf 0x00000001011412d8, we will have the following output showing the return value 0x1 which indicates that the device is Jailbroken.

[….]
[0x00000000]> [dtf onLeave][Tue Aug 29 2023 03:14:15 GMT-0700] 0x00000001011412d8@0x1011412d8 – args: . Retval: 0x1

As seen from the screenshot below, a popup message box will inform us that the device is indeed Jailbroken.

Hooking with r2frida

At this point we have all the necessary information in order to be able to manipulate the output of the isJailbroken method and change the return value to 0x0. This change will alter the behaviour of the application indicating that the device is not Jailbroken.

[0x00000000]> :eval Interceptor.attach(ptr(`:ic JailbreakDetection~isJailbroken[0]`), {onLeave: function (retval) { retval.replace(0x00) }})

{}
[0x00000000]>

Again, from the menu of the DVIA-v2 app go to Jailbreak detection and then choose Jailbreak Test 2. Now from the popup message box we will see that the device is not jailbroken. At this final point we have succesfully bypassed the jailbroken detection at runtime using r2frida.

Share This

Share this post with your friends!