8kSec

ipsw Walkthrough Part 1 - The Swiss Army Knife for iOS/MacOS security research

By 8kSec Research Team
ipsw Walkthrough Series
Part 1 of 2
All parts in this series
  1. 1 ipsw Walkthrough Part 1 - The Swiss Army Knife for iOS/MacOS security research
  2. 2 ipsw Walkthrough Part 2 - The Swiss Army Knife for iOS & MacOS security research

In this first blog post about ipsw tool we will see its basic uses and how it can make our life a lot easier. ipsw is a tool that can be used for *OS research, it provides a lot of functionalities related to dyld_shared_cache, DeviceTree, kernelcache, Img4, etc. which are all of interest to security researchers.

We will go over each of command to see what it does and how to use it, but before we do that, we first need to get ipsw. We can build ipsw from the source or we can download one of the prebuilt binaries available on the GitHub repository.

After we have downloaded ipsw, to confirm that everything is working okay and to see the list of the options, we will type --help .

IPSW tool help output showing all available commands and options

appstore

appstore is the first command that ipsw provides and it allows us to interact with the App Store Connect API.

IPSW appstore command help output showing App Store Connect API subcommands

ipsw appstore provides:

  • managing bundle IDs of the created applications
  • management of signing certificates
  • listing and registering new devices
  • managing provisioning profiles
  • generating jwt token that we can use to interact with App Store Connect API.

A lot of ipsw commands require App Store Connect API key and we can obtain one by visiting https://appstoreconnect.apple.com/access/api.

App Store Connect API Keys page showing how to create and download an API key with Issuer ID and Key ID

After the key is created we need to download it, by clicking on the Download button.

Path to the file will be provided with the -p flag, Issuer ID with the -i flag and Key ID with -k flag respectively, both of these IDs can be copied from the webpage as can be seen on the image above.

bundle

We can list registered bundle IDs (created with Xcode) using ipsw appstore bundle ls while providing previously acquired information (.p8 file, Issuer ID and Key ID). We could use this command in order to see all the bundle IDs that we have registered.

command: ipsw appstore bundle ls -p PATH_TO_P8_FILE -i ISSUER_ID -k KEY_ID

IPSW appstore bundle ls command output listing registered bundle IDs

cert

appstore provides us with the management of signing certificates. To list the certificates we can simply call ipsw appstore cert ls with the .p8 file, Issuer ID and Key ID. We can use this command in the cases where we would want to quickly create new signing certificate or if we want to check them.

command: ipsw appstore cert ls -p PATH_TO_P8_FILE -i ISSUER_ID -k KEY_ID

IPSW appstore cert ls command output listing signing certificates

Now we will see how to create new signing certificate. The first thing we need to do is to create new key using openssl.

command: openssl req -newkey rsa:2048 -keyout 8ksec.key -out 8ksec.csr

After filling required information we will have Certificate Signing Request inside of 8ksec.csr file and private key inside of 8ksec.key. The contents of the 8ksec.csr will be passed to the -c flag of ipsw appstore cert add. We need to clean it up a bit by removing newlines, we can do that with tr -d ’n’.

OpenSSL command generating a CSR and private key for a signing certificate

After we have obtained our csr we will call ipsw appstore cert add with the --type DEVELOPMENT.

command: ipsw appstore cert add -c CONTENTS_OF_CSR -t DEVELOPMENT -p PATH_TO_P8_FILE -i ISSUER_ID -k KEY_ID

IPSW appstore cert add command uploading a new signing certificate

We can see that we successfully created a new signing certificate which got saved to the file on our system. To confirm that it is indeed created, let’s call ipsw appstore cert ls one more time.

IPSW appstore cert ls output confirming the newly added signing certificate appears in the list

To remove the certificate, we simply pass the --id to ipsw appstore cert rm. In our case the command would look like ipsw appstore cert rm --id Q6FAZ3K5VT -p PATH_TO_P8_FILE -i ISSUER_ID -k KEY_ID.

device

We can also manage devices using appstore subcommand such as listing devices or registering new devices. To add new device, we need to have UDID of the device and specify its name and platform (iOS). UDID of the device can simply be seen using idevice_id from libimobiledevice.

To list the devices command is ipsw appstore device ls -p PATH_TO_P8_FILE -i ISSUER_ID -k KEY_ID

IPSW appstore device ls output listing registered devices by UDID, name, and platform

profile

appstore subcommand provides us with profile subcommand which allows us to manage provisioning profiles. We can do things such as listing, creating, renewing and removing provisioning profiles. Provisioning profiles allow for running apps with entitlements or signing certificates outside of the normal configuration of iOS.

To list provisioning profiles: ipsw appstore profile ls -p PATH_TO_P8_FILE -i ISSUER_ID -k KEY_ID

IPSW appstore profile ls output listing provisioning profiles with their IDs and expiration dates

To create a new provisioning profile we need to pass the following:

  • the name of the provisioning profile
  • certificate ID for the newly created provisioning profile
  • finally, device ID for which device will this new provisioning profile be related

command: ipsw appstore profile create NAME -c CERTIFICATES_ID -d DEVICES_ID -p PATH_TO_P8_FILE -i ISSUER_ID -k KEY_ID

IPSW appstore profile create command generating a new provisioning profile

If the request was successful, provisioning profile will be written to the NAME.mobileprovision on the disk. To confirm that we have indeed created the profile, let’s issue another profile ls.

IPSW appstore profile ls output confirming the newly created provisioning profile

To remove the profile, we simply need to pass the name of the profile to ipsw appstore profile rm with -n flag.

command: ipsw appstore profile rm -n 8ksec -p PATH_TO_P8_FILE -i ISSUER_ID -k KEY_ID

token

Finally, we can generate JWT token to use with the App Store Connect API.

command: ipsw appstore token -p PATH_TO_P8_FILE -i ISSUER_ID -k KEY_ID

IPSW appstore token command generating a JWT token for App Store Connect API authentication

device-list

The second main subcommand that ipsw provides is device-list. It allows us to see the list of all iOS devices, its architectures and the CPU inside of them.

IPSW device list command output showing connected iOS/macOS devices with their identifiers

diff

The third main subcommand is related to diffing two ipsw files. ipsw files are basically firmware files and we can diff or find differences between two ipsw files. This can be useful for example if the vulnerability was patched and we don’t know the exact details, this can point us in the right direction.

We will use iPhone 15 17.0.2 and iPhone 15 7.0.3 ipsw files.

command: ipsw diff ~/Downloads/iPhone15,4_17.0.2_21A350_Restore.ipsw ~/Downloads/iPhone15,4_17.0.3_21A360_Restore.ipsw

IPSW diff command help output showing options for comparing two IPSW files

After running the command, we can see that checks a bunch of different categories, such as dyld_shared_cache, entitlements, etc.

A couple of lines below, we can see some actual changes.

IPSW diff output showing differences between two IPSW builds with changed files highlighted

download

The fourth subcommand allows us to download a whole lot of different stuffs, such as OSs, IPAs, IPSW files, macOS installers and more. We can use this command to go over a bunch of different webpages to download or browse stuff, instead we can do it from the terminal.

IPSW download command help output listing download subcommands for IPSW, OTA, and other firmware files

appledb

This subcommand allows us to download IPSWs files directly from the appledb.

command: ipsw download appledb --os iOS --version '17.0.3' --device iPhone15,2 --kernel

IPSW download appledb command output listing available firmware from the AppleDB database

IPSW download appledb ls output showing firmware entries with build numbers and URLs

dev

dev allows you to download IPSWs, OSs, Xcode and more from the https://developer.apple.com/download.

command: ipsw download dev

Typing the command above will give you a list of what you want to download so you can easily select it.

IPSW download dev command output showing developer beta firmware available for download

macos

Another useful subcommand for ipsw download is macos which allows you to download macOS installers.

IPSW download macOS command output listing available macOS IPSW files for download

ipsw download offers other options such as git, ipa but to leave a room for the rest of the commands, we will skip those.

dtree

This command allows us to parse DeviceTree. DeviceTree is a representation of hardware used by the bootloaders to provide the kernel with a mapping of a hardware.

To parse DeviceTree, we first need to have one and to do that, we can use ipsw extract to extract DeviceTree from the IPSW file.

IPSW extract dtree command extracting the DeviceTree from a kernelcache

Once we have extracted the DeviceTree, we can parse it with ipsw dtree.

command: ipsw dtree PATH_TO_DEVICE_TREE_FILE

IPSW dtree output displaying the parsed DeviceTree structure with hardware properties

dyld

dyld subcommand provides a lot of functionalities related to the dyld_shared_cache. Some of the functionalities are:

  • getting info about the dyld_shared_cache
  • finding which dylibs import another dylib
  • extracting the dyld_shared_cache
  • search for string inside of dyld_shared_cache
  • and a bunch more

dyld stands for dynamic loader and it’s job is to load the shared libraries into the memory of the process. To speed-up the loading process, these are all combined into the single file called dyld shared cache. We will cover a couple of these functionalities as there is a lot of them.

info

To get all information from the dyld_shared_cache we can just call ipsw dyld info with the path to dyld_shared_cache. We also have a couple of optionals, such as printing only dylibs, dlopen closures.

command: ipsw dyld info PATH_TO_DYLD_SHARED_CACHE

IPSW dyld info command output showing dyld_shared_cache metadata including platform and UUID

Also, after some general information we can see dylibs inside of dyld_shared_cache as can be seen on the image below.

IPSW dyld info images output listing all dylib images embedded in the dyld_shared_cache

extract

We can extract specific framework for further analysis from the dyld_shared_cache using ipsw dyld extract command.

command: ipsw dyld extract PATH_TO_DYLD_SHARED_CACHE DYLIB

On the image below, we can see extracting Accelerate.framework.

IPSW dyld extract command extracting a specific dylib from the dyld_shared_cache

macho

This subcommand for ipsw dyld allows us to parse specific dylib inside of dyld_shared_cache file.

command: ipsw dyld macho PATH_TO_DYLD_SHARED_CACHE DYLIB

For example, to get loads commands from the Accelerate.framework, we can do:

IPSW dyld macho command output showing Mach-O header information for a dyld_shared_cache image

imports

To see which dylibs load specific dylib, we can use ipsw dyld imports command. Lets say we have found a vulnerability inside of specific dylib and we want to check which other frameworks/dylibs import it, we can use this command.

command: ipsw dyld imports PATH_TO_DYLD_SHARED_CACHE DYLIB

IPSW dyld imports output listing all dylibs imported by a specific framework in the shared cache

Here we can see which dylibs are loading Accelerate.framework.

objc

We can gather Objective C information from the dylibs using ipsw dyld objc. We can use it to print classes, protocols, selectors or imp-caches for dyld_shared_cache. This command is useful if we want to see some classes/functionalities inside of PrivateFrameworks.

To dump all the classes, we can use ipsw dyld objc —class PATH_TO_DYLD_SHARED_CACHE

IPSW dyld objc command output listing Objective-C classes and protocols from a dyld_shared_cache image

split

We can split the dyld_shared_cache using ipsw dyld split which allows us to see everything inside of dyld_shared_cache. We will go over splitting dyld_shared_cache on macOS Ventura.

On macOS Ventura, dyld_shared_cache is stored at the location of /System/Cryptexes/OS/System/Library/dyld/.

Terminal output showing the file system location of the dyld_shared_cache on macOS Ventura

To do that, we just need to pass the path to the dyld_shared_cache file and optional output path. As we can recall previously, our dyld_shared_cache files are stored at the /System/Cryptexes/OS/System/Library/dyld/ and we will use dyld_shared_cache_arm64e one.

command: ipsw dyld split /System/Cryptexes/Os/System/Library/dyld/dyld_shared_cache_arm64e /tmp/8ksec.

Terminal command extracting a dylib from the local macOS dyld_shared_cache using IPSW

A couple of seconds later, we can examine the output directory to see what is inside.

Terminal showing examination of extracted dylib output file from the dyld_shared_cache

We can see a lot of interesting directories such as PrivateFrameworks and Frameworks. We can now, for example, see the exported symbols from one of this frameworks, let’s say CalendarWidget.framework using nm -Ug followed by the path to the CalendarWidget shared library.

IPSW output showing symbols extracted from the Calendar widget dylib in the shared cache

str

ipsw dyld str allows us to search for specific string patterns inside of dyld_shared_cache files. This comes in handy when we want to check for example where is the specific string used/referenced.

command: ipsw str PATH_TO_DYLD_SHARED_CACHE -p “REGEX PATTERN”

IPSW dyld str command output searching for strings within a dyld_shared_cache image

As can be seen on the image above, there are quite a lot of cases where the world string is present. We can also see the name of the framework where the pattern was found.

ent

ipsw ent allows us to search for specific entitlement inside of IPSW, make a diff of entitlements inside of two different IPSW files as well as dumping entitlements for specific MachO file. This one can be useful if we want to know every binary that contains specific entitlement, or we want to see changes between two different IPSW files.

To do search for specific entitlement, let’s say for com.apple.developer.carplay-audio, the command would look like this:

command: ipsw ent -e com.apple.developer.carplay-audio PATH_TO_IPSW_FILE

IPSW ent search command output searching for entitlements across all binaries in an IPSW

diff

Diff allows us to see difference in entitlements between two IPSW files.

command: ipsw ent --diff PATH_TO_FIRST_IPSW PATH_TO_SECOND_IPSW

IPSW ent diff output comparing entitlement changes between two iOS builds

info

ipsw info command allows us to see information regarding IPSW or OTA file. We could use this command if we want to see the chip or iOS version for the specific IPSW or OTA file. We can do the check for the local files, or we can pass the URL with the -r flag to to analysis on the remote file.

command for local file: ipsw info PATH_TO_IPSW_OTA_FILE command for remote: ipsw info -r URL

IPSW info command output displaying metadata about an IPSW file including build number and device support

mdevs

ipsw mdevs allows us to see all mobile devices inside of specific IPSW file.

command: ipsw mdevs PATH_TO_IPSW

IPSW mdevs command output listing all Apple devices supported by a given IPSW file

mount

ipsw mount command allows us to mount the specific part of IPSW file. Those parts are fs (filesystem), sys (system) or app (library). We may want to use this command if we would like to see what is exactly inside of IPSW file and to do some further analysis/research on the specific part of it, e.g. specific dylib.

command: ipsw mount [fs|sys|app] PATH_TO_IPSW_FILE

On the next two images we can see mounting fs (filesystem) and what is inside of it.

IPSW mount fs command mounting the filesystem partition from an IPSW file

Terminal output showing the mounted iOS filesystem partition contents after running ipsw mount fs

Now, we will mount the sys (system) and we will check its content.

IPSW mount sys command mounting the system partition from an IPSW

Terminal showing the mounted iOS system partition directory listing after ipsw mount sys

Finally, we will mount app.

IPSW mount app command mounting the application partition from an IPSW file

Terminal showing the mounted iOS application partition directory listing after ipsw mount app

plist

ipsw plist command allows us to dump the .plist file as json or for example to watch for changes in plist files inside of specific directory. Let’s say that we are analyzing the application and we want to know what and when triggers writing specific option inside of plist file, we could utilise this command.

file

We can pass in the path to the plist file and it would get parsed as json.

command: ipsw plist PATH_TO_PLIST_FILE

IPSW plist file command output reading a plist file from an IPSW or mounted filesystem

We can also pass the output of this to jq to get let’s say bundle identifier.

IPSW plist file output piped through jq for formatted JSON viewing of a property list

watch

We can also use ipsw plist to watch specific directory for changes inside of plist files. This comes in handy when we want to track what changes inside of plist file while we are working with the application.

command: ipsw plist -w PATH_TO_DIRECTORY

IPSW plist watch command monitoring plist file changes on a connected device

update

ipsw provides us with the convenient way to update it by running the ipsw update which then allows us to download the new version for out specific OS and architecture.

command: ipsw update

IPSW update check output showing the current ipsw tool version before updating

After the download has finished, we can extract the archive, check our version and to see if we are indeed running the latest version by running ipsw update inside of downloaded package.

IPSW update command output confirming the tool has been updated to the latest version

watch

ipsw watch is the command which is not strictly related to iOS or macOS but it is related to watching specific GitHub repository for changes. We can watch for the changed file or perhaps pattern inside of commit messages and get notified about the changes over the Discord.

In the next part, we will look at some additional uses of ipsw for *OS security research.

References:

Get in Touch

Want to learn these techniques hands-on, or need help assessing your own mobile or AI stack? We run live and on-demand trainings, offer mobile-security certifications, and take on penetration-testing engagements. Pick the door that fits.

We respond within one business day. Visit our events page to see where we'll be next.

Recent Blogs