Basic iOS Testing Operations

Getting the UDID of an iOS device

Perform this actions having connected the device to the computer via USB and having the device unlocked.

The UDID is a 40-digit unique sequence of letters and numbers to identify an iOS device. You can find the UDID of your iOS device on macOS Catalina onwards in the Finder app, as iTunes is not available anymore in Catalina. Just select the connected iOS device in Finder and click on the information under the name of the iOS device to iterate through it. Besides the UDID, you can find the serial number, IMEI and other useful information.

If you are using a macOS version before Catalina, you can find the UDID of your iOS device via iTunes, by selecting your device and clicking on "Serial Number" in the summary tab. When clicking on this you will iterate through different metadata of the iOS device including its UDID.

It is also possible to get the UDID via various command line tools on macOS while the device is attached via USB:

  • By using the I/O Registry Explorer tool ioreg:

      $ ioreg -p IOUSB -l | grep "USB Serial"
      |         "USB Serial Number" = "9e8ada44246cee813e2f8c1407520bf2f84849ec"
  • By using ideviceinstaller (also available on Linux):

      $ brew install ideviceinstaller
      $ idevice_id -l
      316f01bd160932d2bf2f95f1f142bc29b1c62dbc
  • By using the system_profiler:

      $ system_profiler SPUSBDataType | sed -n -e '/iPad/,/Serial/p;/iPhone/,/Serial/p;/iPod/,/Serial/p' | grep "Serial Number:"
      2019-09-08 10:18:03.920 system_profiler[13251:1050356] SPUSBDevice: IOCreatePlugInInterfaceForService failed 0xe00002be
                  Serial Number: 64655621de6ef5e56a874d63f1e1bdd14f7103b1
  • By using instruments:

      $ instruments -s devices

Accessing the Device Shell

After jailbreaking the device you should have installed some new app manager like Cydia.

SSH

In order to enable SSH access to your iOS device you can install the OpenSSH package. Once installed, you can access your device via ssh running ssh root@<device_ip_address>, which will log you in as the root user:

$ ssh root@192.168.197.234
root@192.168.197.234's password:
iPhone:~ root#

When accessing your iOS device via SSH consider the following:

  • The default users are root and mobile.

  • The default password for both is alpine.

Remember to change the default password for both users root and mobile as anyone on the same network can find the IP address of your device and connect via the well-known default password, which will give them root access to your device.

****

Connect to a Device via SSH over USB

During a real black box test, a reliable Wi-Fi connection may not be available. In this situation, you can use usbmuxd to connect to your device's SSH server via USB.

Connect macOS to an iOS device by installing and starting iproxy:

$ brew install libimobiledevice
$ iproxy 2222 22
waiting for connection

The above command maps port 22 on the iOS device to port 2222 on localhost. You can also make iproxy run automatically in the background if you don't want to run the binary every time you want to SSH over USB.

With the following command in a new terminal window, you can connect to the device:

$ ssh -p 2222 root@localhost
root@localhost's password:
iPhone:~ root#

Small note on USB of an iDevice: on an iOS device you cannot make data connections anymore after 1 hour of being in a locked state, unless you unlock it again due to the USB Restricted Mode, which was introduced with iOS 11.4.1

On-device Shell App

While usually using an on-device shell (terminal emulator) might be very tedious compared to a remote shell, it can prove handy for debugging in case of, for example, network issues or check some configuration. For example, you can install NewTerm 2 via Cydia for this purpose (it supports iOS 6.0 to 12.1.2 at the time of this writing).

In addition, there are a few jailbreaks that explicitly disable incoming SSH for security reasons. In those cases, it is very convenient to have an on-device shell app, which you can use to first SSH out of the device with a reverse shell, and then connect from your host computer to it.

Opening a reverse shell over SSH can be done by running the command ssh -R <remote_port>:localhost:22 <username>@<host_computer_ip>.

On the on-device shell app run the following command and, when asked, enter the password of the mstg user of the host computer:

ssh -R 2222:localhost:22 mstg@192.168.197.235

On your host computer run the following command and, when asked, enter the password of the root user of the iOS device:

$ ssh -p 2222 root@localhost

Forgotten Password

If you forget your password and want to reset it to the default alpine:

  1. Edit the file /private/etc/master.password on your jailbroken iOS device (using an on-device shell as shown below)

  2. Find the lines:

     root:xxxxxxxxx:0:0::0:0:System Administrator:/var/root:/bin/sh
     mobile:xxxxxxxxx:501:501::0:0:Mobile User:/var/mobile:/bin/sh
  3. Change xxxxxxxxx to /smx7MYTQIi2M (which is the hashed password alpine)

  4. Save and exit

Data Transfer

Copying App Data Files via SSH and SCP

As we know now, files from our app are stored in the Data directory. You can now simply archive the Data directory with tar and pull it from the device with scp:

iPhone:~ root# tar czvf /tmp/data.tgz /private/var/mobile/Containers/Data/Application/8C8E7EB0-BC9B-435B-8EF8-8F5560EB0693
iPhone:~ root# exit
$ scp -P 2222 root@localhost:/tmp/data.tgz .

Using iFunbox

****iFunbox is a GUI application that can be used for several things (uploading/downloading files among them). Another GUI tool for this purpose is iExplorer.

Starting in iOS version 8.4, Apple has restricted the third-party managers to access to the application sandbox, so tools like iFunbox and iExplorer no longer display/retrieve files from apps installed on the device if the device isn't jailbroken.

Using Objection

When you are starting objection (objection --gadget com.apple.mobilesafari explorer) you will find the prompt within the Bundle directory.

org.owasp.MSTG on (iPhone: 10.3.3) [usb] # pwd print
Current directory: /var/containers/Bundle/Application/DABF849D-493E-464C-B66B-B8B6C53A4E76/org.owasp.MSTG.app

Use the env command to get the directories of the app and navigate to the Documents directory.

org.owasp.MSTG on (iPhone: 10.3.3) [usb] # cd /var/mobile/Containers/Data/Application/72C7AAFB-1D75-4FBA-9D83-D8B4A2D44133/Documents
/var/mobile/Containers/Data/Application/72C7AAFB-1D75-4FBA-9D83-D8B4A2D44133/Documents

With the command file download <filename> you can download a file from the iOS device to your host computer and can analyze it afterwards.

org.owasp.MSTG on (iPhone: 10.3.3) [usb] # file download .com.apple.mobile_container_manager.metadata.plist
Downloading /var/mobile/Containers/Data/Application/72C7AAFB-1D75-4FBA-9D83-D8B4A2D44133/.com.apple.mobile_container_manager.metadata.plist to .com.apple.mobile_container_manager.metadata.plist
Streaming file from device...
Writing bytes to destination...
Successfully downloaded /var/mobile/Containers/Data/Application/72C7AAFB-1D75-4FBA-9D83-D8B4A2D44133/.com.apple.mobile_container_manager.metadata.plist to .com.apple.mobile_container_manager.metadata.plist

You can also upload files to the iOS device with file upload <local_file_path>.

Obtaining and Extracting Apps

During development, apps are sometimes provided to testers via over-the-air (OTA) distribution. In that situation, you'll receive an itms-services link, such as the following:

itms-services://?action=download-manifest&url=https://s3-ap-southeast-1.amazonaws.com/test-uat/manifest.plist

You can use the ITMS services asset downloader tool to download the IPA from an OTA distribution URL. Install it via npm:

$ npm install -g itms-services

Save the IPA file locally with the following command:

# itms-services -u "itms-services://?action=download-manifest&url=https://s3-ap-southeast-1.amazonaws.com/test-uat/manifest.plist" -o - > out.ipa

Acquiring the App Binary

  1. From an IPA:

    If you have the IPA (probably including an already decrypted app binary), unzip it and you are ready to go. The app binary is located in the main bundle directory (.app), e.g. Payload/Telegram X.app/Telegram X. See the following subsection for details on the extraction of the property lists.

    On macOS's Finder, .app directories are opened by right-clicking them and selecting "Show Package Content". On the terminal you can just cd into them.

  2. From a Jailbroken device:

    If you don't have the original IPA, then you need a jailbroken device where you will install the app (e.g. via App Store). Once installed, you need to extract the app binary from memory and rebuild the IPA file. Because of DRM, the app binary file is encrypted when it is stored on the iOS device, so simply pulling it from the Bundle (either through SSH or Objection) will not be sufficient to reverse engineer it (read next section).

Decryption (Manual)

Unlike an Android Application, the binary of an iOS app can only be disassembled and not decompiled. When an application is submitted to the app store, Apple first verifies the app conduct and before releasing it to the app-store, Apple encrypts the binary using FairPlay. So the binary download from the app store is encrypted complicating ting the reverse-engineering tasks.

However, note that there are other third party software that can be used to obfuscate the resulting binaries.

In order to run the encrypted binary, the device needs to decrypt it in memory. Then, it's possible to dump the decrypted binary from the memory.

First, check if the binary is compiled with the PIE (Position Independent Code) flag:

otool -Vh Original_App #Check the last word of the last line of this code
Home:
Mach header
      magic  cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64   X86_64        ALL  0x00     EXECUTE    47       6080   NOUNDEFS DYLDLINK TWOLEVEL PIE

If it's set you can use the script change_macho_flags.py to remove it with python2:

python change_mach_o_flags.py --no-pie Original_App
otool -Vh Hello_World
Hello_World:
Mach header
      magic  cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
   MH_MAGIC      ARM         V7  0x00     EXECUTE    22       2356   NOUNDEFS DYLDLINK TWOLEVEL MH_NO_HEAP_EXECUTION

Now that the PIE flag isn't set, the OS will load the program at a fixed starting location every-time. In order to find this location you can use:

otool -l Original_App | grep -A 3 LC_SEGMENT | grep -A 1 __TEXT
  segname __TEXT
   vmaddr 0x00004000

Then, it's necessary to extract the the memory range that needs to be dumped:

otool -l Original_App | grep -A 4 LC_ENCRYPTION_INFO
          cmd LC_ENCRYPTION_INFO
      cmdsize 20
     cryptoff 16384
    cryptsize 17416192
      cryptid 0

The value of cryptoff indicated the starting address of the encrypted content and the cryptsize indicates the size of the encrypted content.

So, the start address to dump will be vmaddr + cryptoff and the end address will be the start address + cryptsize In this case: start_address = 0x4000 + 0x4000 = 0x8000 __and end_address = 0x8000 + 0x109c000 = 0x10a4000

With this information it's just necessary to run the application in the jailbroken device, attach to the process with gdb (gdb -p <pid>) and dump the memory:

dump memory dump.bin 0x8000 0x10a4000

Congrats! You have decrypted the encrypted section in dump.bin. Now transfer this dump to your computer and overwrite the encrypted section with the decrypted one:

dd bs=1 seek=<starting_address> conv=notrunc if=dump.bin of=Original_App

There is one more step to complete. The application is still indicating in its metadata that it's encrypted, but it isn't. Then, when executed, the device will try to decrypt the already decrypted section and it's going to fail. However, you can use tools like MachOView to change this info. Just open the binary and set the cryptid to 0:

Decryption (Automatically)

frida-ios-dump

You can use tools like frida-ios-dump to automatically remove the encryption and an app.

First, make sure that the configuration in Frida-ios-dump dump.py is set to either localhost with port 2222 when using iproxy, or to the actual IP address and port of the device from which you want to dump the binary.

Now you can safely use the tool to enumerate the apps installed:

$ python dump.py -l
 PID  Name             Identifier
----  ---------------  -------------------------------------
 860  Cydia            com.saurik.Cydia
1130  Settings         com.apple.Preferences
 685  Mail             com.apple.mobilemail
 834  Telegram         ph.telegra.Telegraph
   -  Stocks           com.apple.stocks
   ...

and you can dump one of the listed binaries:

$ python3 dump.py -u "root" -p "<PASSWORD>" ph.telegra.Telegraph

Start the target app ph.telegra.Telegraph
Dumping Telegram to /var/folders/qw/gz47_8_n6xx1c_lwq7pq5k040000gn/T
[frida-ios-dump]: HockeySDK.framework has been loaded.
[frida-ios-dump]: Load Postbox.framework success.
[frida-ios-dump]: libswiftContacts.dylib has been dlopen.
...
start dump /private/var/containers/Bundle/Application/14002D30-B113-4FDF-BD25-1BF740383149/Telegram.app/Frameworks/libswiftsimd.dylib
libswiftsimd.dylib.fid: 100%|██████████| 343k/343k [00:00<00:00, 1.54MB/s]
start dump /private/var/containers/Bundle/Application/14002D30-B113-4FDF-BD25-1BF740383149/Telegram.app/Frameworks/libswiftCoreData.dylib
libswiftCoreData.dylib.fid: 100%|██████████| 82.5k/82.5k [00:00<00:00, 477kB/s]
5.m4a: 80.9MB [00:14, 5.85MB/s]
0.00B [00:00, ?B/s]Generating "Telegram.ipa"

After this, the Telegram.ipa file will be created in your current directory. You can validate the success of the dump by removing the app and reinstalling it (e.g. using ios-deploy ios-deploy -b Telegram.ipa). Note that this will only work on jailbroken devices, as otherwise the signature won't be valid.

flexdecrypt

In order to obtain the ipa file from an installed application you can also use the tool flexdecrypt or a wrapper of the tool called flexdump. In any case you will need to install flexdecrypt in the device running something like:

wget https://github.com/JohnCoates/flexdecrypt/releases/download/1.1/flexdecrypt.deb
dpkg -i flexdecrypt.deb
rm flexdecrypt.deb

and in order to use flexdump:

apt install zip unzip
wget https://gist.githubusercontent.com/defparam/71d67ee738341559c35c684d659d40ac/raw/30c7612262f1faf7871ba8e32fbe29c0f3ef9e27/flexdump -P /usr/local/bin; chmod +x /usr/local/bin/flexdump
flexdump list #List apps
flexdump dump Twitter.app #Create .ipa file from app

Installing Apps

When you install an application without using Apple's App Store, this is called sideloading. There are various ways of sideloading which are described below. On the iOS device, the actual installation process is then handled by the installd daemon, which will unpack and install the application. To integrate app services or be installed on an iOS device, all applications must be signed with a certificate issued by Apple. This means that the application can be installed only after successful code signature verification. On a jailbroken phone, however, you can circumvent this security feature with AppSync, a package available in the Cydia store. It contains numerous useful applications that leverage jailbreak-provided root privileges to execute advanced functionality. AppSync is a tweak that patches installd, allowing the installation of fake-signed IPA packages.

Different methods exist for installing an IPA package onto an iOS device, which are described in detail below.

Please note that iTunes is no longer available in macOS Catalina. If you are using an older version of macOS, iTunes is still available but since iTunes 12.7 it is not possible to install apps.

Cydia Impactor

Cydia Impactor was originally created to jailbreak iPhones, but has been rewritten to sign and install IPA packages to iOS devices via sideloading (and even APK files to Android devices). Cydia Impactor is available for Windows, macOS and Linux. A step by step guide and troubleshooting steps are available on yalujailbreak.net.

libimobiledevice

On Linux and also macOS, you can alternatively use libimobiledevice, a cross-platform software protocol library and a set of tools for native communication with iOS devices. This allows you to install apps over a USB connection by executing ideviceinstaller. The connection is implemented with the USB multiplexing daemon usbmuxd, which provides a TCP tunnel over USB.

The package for libimobiledevice will be available in your Linux package manager. On macOS you can install libimobiledevice via brew:

$ brew install libimobiledevice
$ brew install ideviceinstaller

After the installation you have several new command line tools available, such as ideviceinfo, ideviceinstaller or idevicedebug.

# The following command will show detailed information about the iOS device connected via USB.
$ ideviceinfo
# The following command will install the IPA to your iOS device.
$ ideviceinstaller -i iGoat-Swift_v1.0-frida-codesigned.ipa
...
Install: Complete
# The following command will start the app in debug mode, by providing the bundle name. The bundle name can be found in the previous command after "Installing".
$ idevicedebug -d run OWASP.iGoat-Swift

ipainstaller

The IPA can also be directly installed on the iOS device via the command line with ipainstaller. After copying the file over to the device, for example via scp, you can execute ipainstaller with the IPA's filename:

$ ipainstaller App_name.ipa

ios-deploy

On macOS you can also use the ios-deploy tool to install iOS apps from the command line. You'll need to unzip your IPA since ios-deploy uses the app bundles to install apps.

$ unzip Name.ipa
$ ios-deploy --bundle 'Payload/Name.app' -W -d -v

After the app is installed on the iOS device, you can simply start it by adding the -m flag which will directly start debugging without installing the app again.

$ ios-deploy --bundle 'Payload/Name.app' -W -d -v -m

Xcode

It is also possible to use the Xcode IDE to install iOS apps by doing the following steps:

  1. Start Xcode

  2. Select Window/Devices and Simulators

  3. Select the connected iOS device and click on the + sign in Installed Apps.

Allow Application Installation on a Non-iPad Device

Sometimes an application can require to be used on an iPad device. If you only have iPhone or iPod touch devices then you can force the application to accept to be installed and used on these kinds of devices. You can do this by changing the value of the property UIDeviceFamily to the value 1 in the Info.plist file.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>

  <key>UIDeviceFamily</key>
  <array>
    <integer>1</integer>
  </array>

</dict>
</plist>

It is important to note that changing this value will break the original signature of the IPA file so you need to re-sign the IPA, after the update, in order to install it on a device on which the signature validation has not been disabled.

This bypass might not work if the application requires capabilities that are specific to modern iPads while your iPhone or iPod is a bit older.

Possible values for the property UIDeviceFamily can be found in the Apple Developer documentation.

Last updated