/ios-penetration-testing-cheat-sheet

Work in progress...

Primary LanguageJavaScriptMIT LicenseMIT

iOS Penetration Testing Cheat Sheet

This is more of a checklist for myself. May contain useful tips and tricks.

Everything was tested on Kali Linux v2023.1 (64-bit) and iPhone 7 with iOS v13.4.1 and unc0ver jailbreak v8.0.2.

Check 3uTools if you want to jailbreak your iOS device. I have no liability over your actions.

For help with any of the tools type <tool_name> [-h | -hh | --help] or man <tool_name>.

If you didn't already, read OWASP MSTG and OWASP MASVS. You can download OWASP MSTG checklist from here.

Highly recommend reading Hacking iOS Applications and HackTricks - iOS Pentesting.

Websites that you should use while writing the report:

My other cheat sheets:

Future plans:

  • install Burp Proxy and ZAP certificates,
  • test widgets, push notifications, app extensions, and Firebase,
  • disassemble, reverse engineer, and resign IPA,
  • restore from a backup,
  • future downgrades using SHSH BLOBS.

Table of Contents

0. Install Tools

1. Basics

2. Inspect an IPA

3. Search for Files and Directories

4. Inspect Files

5. Property Lister

6. Deeplinks

7. Frida

8. Objection

9. Repackage an IPA

10. Miscellaneous

11. Tips and Security Best Practices

12. Useful Websites and Tools

0. Install Tools

Cydia Sources and Tools

Add the following sources to Cydia:

Install required tools on your iOS device using Cydia:

  • A-Bypass
  • AppSync Unified
  • Cycript
  • Cydia Substrate
  • Debian Packager
  • Frida (fix v16+ installation issue)
  • nano
  • PreferenceLoader
  • ReProvision Reborn
  • SQLite 3.x
  • wget
  • zip

SSL Kill Switch 2

SSH to your iOS device, then, download and install SSL Kill Switch 2:

wget https://github.com/nabla-c0d3/ssl-kill-switch2/releases/download/0.14/com.nablac0d3.sslkillswitch2_0.14.deb

dpkg -i com.nablac0d3.sslkillswitch2_0.14.deb

killall -HUP SpringBoard

Uninstall SSL Kill Switch 2:

dpkg -r --force-all com.nablac0d3.sslkillswitch2

Kali Linux Tools

Install required tools on your Kali Linux:

apt-get -y install docker.io

systemctl start docker

apt-get -y install ideviceinstaller libimobiledevice-utils libplist-utils radare2 sqlite3 sqlitebrowser xmlstarlet

pip3 install frida-tools objection property-lister

If you are interested in my tool, check /ivan-sincek/property-lister.

Make sure that Frida and Objection are always up to date:

pip3 install frida-tools objection property-lister --upgrade

Mobile Security Framework (MobSF)

Install:

docker pull opensecurity/mobile-security-framework-mobsf

Run:

docker run -it --rm --name mobsf -p 8000:8000 opensecurity/mobile-security-framework-mobsf

Navigate to http://localhost:8000 using your preferred web browser.

Uninstall:

docker image rm opensecurity/mobile-security-framework-mobsf

1. Basics

Install/Uninstall an IPA

Install an IPA:

ideviceinstaller -i someapp.ipa

Uninstall an IPA:

ideviceinstaller -U com.someapp.dev

Install an IPA using 3uTools desktop app. Jailbreak is required.

Sideloading an IPA using 3uTools

Figure 1 - Sideloading an IPA using 3uTools


On your Kali Linux, start a local web server, and put an IPA in the web root directory (e.g. somedir):

mkdir somedir

python3 -m http.server 9000 --directory somedir

On your iOS device, download the IPA, long press on it, choose "Share", and install it using ReProvision Reborn iOS app. Jailbreak is required.

Sideloading an IPA using ReProvision Reborn

Figure 2 - Sideloading an IPA using ReProvision Reborn

SSH to Your iOS Device

ssh root@192.168.1.10

Default password is alpine.

Download/Upload Files and Directories

Tilde ~ is short for the root directory.

Download a file or directory from your iOS device:

scp root@192.168.1.10:~/somefile.txt ./

scp -r root@192.168.1.10:~/somedir ./

Upload a file or directory to your iOS device:

scp somefile.txt root@192.168.1.10:~/

scp -r somedir root@192.168.1.10:~/

Use nano to edit files directly on your iOS device.

2. Inspect an IPA

Pull a Decrypted IPA

Pull a decrypted IPA from your iOS device:

git clone https://github.com/AloneMonkey/frida-ios-dump && cd frida-ios-dump && pip3 install -r requirements.txt

python3 dump.py -o decrypted.ipa -P alpine -p 22 -H 192.168.1.10 com.someapp.dev

If you want to pull an encrypted IPA from your iOS device, see section 8. Repackage an IPA and iMazing.

To unpack e.g. someapp.ipa or decrypted.ipa (preferred), run:

unzip decrypted.ipa

You should now see the unpacked Payload directory.

Binary

Navigate to /Payload/someapp.app/ directory. There, you will find a binary which have the same name and no file type (i.e. someapp).

Search the binary for specific keywords:

rabin2 -zzzqq someapp | grep -Pi 'keyword'

rabin2 -zzzqq someapp | grep -Pi 'hasOnlySecureContent|javaScriptEnabled|UIWebView|WKWebView'

WebViews can sometimes be very subtle, e.g. they could be hidden as a link to terms of agreement, privacy policy, about the software, referral, etc.

Search the binary for endpoints, deeplinks, sensitive data, comments, etc. For more examples, see section 4. Inspect Files.

Search the binary for weak hash algorithms, insecure random functions, insecure memory allocation functions, etc. For the best results, use MobSF.


Download the latest AppInfoScanner, install the requirements, and then extract and resolve endpoints from the binary:

pip3 install -r requirements.txt

python3 app.py ios -i someapp

Info.plist

Navigate to /Payload/someapp.app/ directory. There, you will find a property list file with the name Info.plist.

Extract URL schemes from the property list file:

xmlstarlet sel -t -v 'plist/dict/array/dict[key = "CFBundleURLSchemes"]/array/string' -nl Info.plist | sort -uf | tee url_schemes.txt

Search the property list file for endpoints, sensitive data [in Base64 encoding], etc. For more examples, see section 4. Inspect Files.

3. Search for Files and Directories

Search for files and directories from the root directory:

find / -iname '*keyword*'

Search for files and directories in the app specific directories (run env in Objection):

cd /private/var/containers/Bundle/Application/XXX...XXX/

cd /var/mobile/Containers/Data/Application/YYY...YYY/

If you want to download a whole directory from your iOS device, see section Download/Upload Files and Directories.

I preffer downloading the app specific directories, and then doing the file inspection on my Kali Linux.

Search for files and directories from the current directory:

find . -iname '*keyword*'

for keyword in 'access' 'account' 'admin' 'card' 'cer' 'conf' 'cred' 'customer' 'email' 'history' 'info' 'json' 'jwt' 'key' 'kyc' 'log' 'otp' 'pass' 'pem' 'pin' 'plist' 'priv' 'refresh' 'salt' 'secret' 'seed' 'setting' 'sign' 'sql' 'token' 'transaction' 'transfer' 'tar' 'txt' 'user' 'zip' 'xml'; do find . -iname "*${keyword}*"; done

NSUserDefaults

Search for files and directories in NSUserDefaults insecure storage directory:

cd /var/mobile/Containers/Data/Application/YYY...YYY/Library/Preferences/

Search for sensitive data in property list files inside NSUserDefaults insecure storage directory:

scp root@192.168.1.10:/var/mobile/Containers/Data/Application/YYY...YYY/Library/Preferences/com.someapp.dev.plist ./

plistutil -f xml -i com.someapp.dev.plist

4. Inspect Files

Inspect memory dumps, binaries, files inside an unpacked IPA, files inside the app specific directories, or any other files.

After you finish testing [and logout], don't forget to download the app specific directories and inspect all the files inside. Inspect what is new and what still persists after logout.

Don't forget to extract Base64 strings from property list files as you might find sensitive data.

There will be some false positive results since the regular expressions are not perfect. I prefer to use rabin2 over strings because it can read Unicode characters.

On your iOS device, try to modify app's files to test the filesystem checksum validation, i.e. to test the file integrity validation.

Single File

Extract hardcoded sensitive data:

rabin2 -zzzqq somefile | grep -Pi '[^\w\d]+(basic|bearer)\ .+'

rabin2 -zzzqq somefile | grep -Pi '(access|account|admin|basic|bearer|card|conf|cred|customer|email|history|id|info|jwt|key|kyc|log|otp|pass|pin|priv|refresh|salt|secret|seed|setting|sign|token|transaction|transfer|user)\w*(?:\"\ *\:|\ *\=).+'

rabin2 -zzzqq somefile | grep -Pi '([^\w\d]+(to(\_|\ )do|todo|note)\ |\/\/|\/\*|\*\/).+'

Extract URLs, deeplinks, IPs, etc.:

rabin2 -zzzqq somefile | grep -Po '\w+\:\/\/[\w\-\.\@\:\/\?\=\%\&\#]+' | grep -Piv '\.(css|gif|jpeg|jpg|ogg|otf|png|svg|ttf|woff|woff2)' | sort -uf | tee urls.txt

rabin2 -zzzqq somefile | grep -Po '(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}' | sort -uf | tee ips.txt

Extract all strings and decode Base64 strings:

rabin2 -zzzqq somefile | sort -uf > strings.txt

grep -Po '(?:([a-zA-Z0-9\+\/]){4})*(?:(?1){4}|(?1){3}\=|(?1){2}\=\=)' strings.txt | sort -uf > base64.txt

for string in $(cat base64.txt); do res=$(echo "${string}" | base64 -d 2>/dev/null | grep -PI '[\s\S]+'); if [[ ! -z $res ]]; then echo -n "${string}\n${res}\n\n"; fi; done | tee base64_decoded.txt

Multiple Files

Extract hardcoded sensitive data:

IFS=$'\n'; for file in $(find . -type f); do echo -n "\nFILE: \"${file}\"\n"; rabin2 -zzzqq "${file}" 2>/dev/null | grep -Pi '[^\w\d]+(basic|bearer)\ .+'; done

IFS=$'\n'; for file in $(find . -type f); do echo -n "\nFILE: \"${file}\"\n"; rabin2 -zzzqq "${file}" 2>/dev/null | grep -Pi '(access|account|admin|basic|bearer|card|conf|cred|customer|email|history|id|info|jwt|key|kyc|log|otp|pass|pin|priv|refresh|salt|secret|seed|setting|sign|token|transaction|transfer|user)\w*(?:\"\ *\:|\ *\=).+'; done

IFS=$'\n'; for file in $(find . -type f); do echo -n "\nFILE: \"${file}\"\n"; rabin2 -zzzqq "${file}" 2>/dev/null | grep -Pi '([^\w\d]+(to(\_|\ )do|todo|note)\ |\/\/|\/\*|\*\/).+'; done

Extract URLs, deeplinks, IPs, etc.:

IFS=$'\n'; for file in $(find . -type f); do rabin2 -zzzqq "${file}" 2>/dev/null; done | grep -Po '\w+\:\/\/[\w\-\.\@\:\/\?\=\%\&\#]+' | grep -Piv '\.(css|gif|jpeg|jpg|ogg|otf|png|svg|ttf|woff|woff2)' | sort -uf | tee urls.txt

IFS=$'\n'; for file in $(find . -type f); do rabin2 -zzzqq "${file}" 2>/dev/null; done | grep -Po '(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}' | sort -uf | tee ips.txt

Extract all strings and decode Base64 strings:

IFS=$'\n'; for file in $(find . -type f); do rabin2 -zzzqq "${file}" 2>/dev/null; done | sort -uf > strings.txt

grep -Po '(?:([a-zA-Z0-9\+\/]){4})*(?:(?1){4}|(?1){3}\=|(?1){2}\=\=)' strings.txt | sort -uf > base64.txt

for string in $(cat base64.txt); do res=$(echo "${string}" | base64 -d 2>/dev/null | grep -PI '[\s\S]+'); if [[ ! -z $res ]]; then echo -n "${string}\n${res}\n\n"; fi; done | tee base64_decoded.txt

SQLite 3

Use SCP to download database files, and then open them using DB Browser for SQLite.

To inspect the content, navigate to Browse Data tab, expand Table dropdown menu, and select the desired table.

SQLite

Figure 3 - DB Browser for SQLite


To inspect/edit database files on your iOS device, use SQLite 3; SSH to your iOS device and run the following commands:

sqlite3 somefile

.dump

.tables

SELECT * FROM sometable;

.quit

You can automate such tasks with Property Lister.

Backups

Get your iOS device UDID:

idevice_id -l

Create a backup:

idevicebackup2 backup --full --source someudid --udid someudid ./

5. Property Lister

Cache.db

By default, NSURLSession class stores data such as HTTP requests and responses in Cache.db unencrypted database file.

Search for sensitive data in property list files inside Cache.db unencrypted database file:

scp root@192.168.1.10:/var/mobile/Containers/Data/Application/YYY...YYY/Library/Caches/com.someapp.dev/Cache.db ./

property-lister -db Cache.db -o plists

Cache.db is unencrypted and backed up by default, and as such, should not contain any sensitive data after user logs out - it should be cleard by calling removeAllCachedResponses().

Find All / Dump All

Unpack e.g. someapp.ipa or decrypted.ipa (preferred).

Dump all the databases, and extract and convert to a readable format all the property list files:

property-lister -db Payload -o ipa_db

property-lister -pl Payload -o ipa_pl

Repeat the same for the app specific directories.

6. Deeplinks

Create an HTML template to manually test deeplinks:

mkdir ios_deeplinks

# multiple URL schemes

for scheme in $(cat url_schemes.txt); do for url in $(cat urls.txt | grep -Poi "${scheme}\:\/\/.+"); do if [[ ! -z $url ]]; then echo -n "<a href='${url}'>${url}</a>\n<br><br>\n" | tee "ios_deeplinks/${scheme}_deeplinks.html"; fi; done; done

# single URL scheme

scheme="somescheme"; for string in $(cat urls.txt | grep -Poi "${scheme}\:\/\/.+"); do echo -n "<a href='${string}'>${string}</a>\n<br><br>\n"; done | tee "ios_deeplinks/${scheme}_deeplinks.html"

python3 -m http.server 9000 --directory ios_deeplinks

For url_schemes.txt see section Info.plist, and for urls.txt see section 4. Inspect Files.


Fuzz deeplinks using ios-url-scheme-fuzzing script with Frida:

frida -U -no-pause -l ios-url-scheme-fuzzing.js -f com.someapp.dev

frida -U -no-pause --codeshare ivan-sincek/ios-url-scheme-fuzzing -f com.someapp.dev

Check the source code for more instructions. You can also paste the whole source code directly into Frida and call the methods as you prefer.


Sometimes, deeplinks can bypass biometrics.

7. Frida

Useful resources:

List processes:

frida-ps -Uai

frida-ps -Uai | grep -i 'keyword'

Get PID for a specified keyword:

frida-ps -Uai | grep -i 'keyword' | cut -d ' ' -f 1

Discover internal methods/calls:

frida-discover -U -f com.someapp.dev | tee frida_discover.txt

Trace internal methods/calls:

frida-trace -U -p 1337

frida-trace -U -p 1337 -i 'recv*' -i 'send*'

Frida Scripts

Bypass biometrics using ios-touch-id-bypass script:

frida -U -no-pause -l ios-touch-id-bypass.js -f com.someapp.dev

frida -U -no-pause --codeshare ivan-sincek/ios-touch-id-bypass -f com.someapp.dev

On the touch ID prompt, press Cancel.

I prefer to use the built-in method in Objection.


Hook all classes and methods using ios-hook-classes-methods script:

frida -U -no-pause -l ios-hook-classes-methods.js -f com.someapp.dev

frida -U -no-pause --codeshare ivan-sincek/ios-hook-classes-methods -f com.someapp.dev

8. Objection

Useful resources:

Run:

objection -g com.someapp.dev explore

Run a Frida script in Objection:

import somescript.js

objection -g com.someapp.dev explore --startup-script somescript.js

Get information:

ios info binary

ios plist cat Info.plist

Get environment variables:

env

Get HTTP cookies:

ios cookies get

Dump Keychain, NSURLCredentialStorage, and NSUserDefaults:

ios keychain dump

ios nsurlcredentialstorage dump

ios nsuserdefaults get

Sensitive data such as app's PIN, password, etc. should not be stored as a plain-text in the keychain; instead, they should be hashed as an additional level of protection.

Dump app's memory to a file:

memory dump all mem.dmp

Dump app's memory after e.g. 10 minutes of inactivity, then, check if sensitive data is still in the memory, see section 4. Inspect Files.

Search app's memory directly:

memory search 'somestring' --string

List classes and methods:

ios hooking list classes
ios hooking search classes 'keyword'

ios hooking list class_methods 'someclass'
ios hooking search methods 'keyword'

Hook on a class or method:

ios hooking watch class 'someclass'

ios hooking watch method '-[someclass somemethod]' --dump-args --dump-backtrace --dump-return

Change the method's return value:

ios hooking set return_value '-[someclass somemethod]' false

Monitor crypto libraries:

ios monitor crypto

Monitor the pasteboard:

ios pasteboard monitor

You can also dump the pasteboard using cycript.

Bypasses

Bypass a jailbreak detection:

ios jailbreak disable --quiet

objection -g com.someapp.dev explore --startup-command 'ios jailbreak disable --quiet'

Also, on your iOS device, check A-Bypass in Settings app.


Bypass SSL pinning:

ios sslpinning disable --quiet

objection -g com.someapp.dev explore --startup-command 'ios sslpinning disable --quiet'

Also, on your iOS device, check SSL Kill Switch 2 in Settings app.


Bypass biometrics:

ios ui biometrics_bypass --quiet

objection -g com.someapp.dev explore --startup-command 'ios ui biometrics_bypass --quiet'

Also, you can import Frida script.

9. Repackage an IPA

SSH to your iOS device and run the following commands.

Navigate to the app specific directory:

cd /private/var/containers/Bundle/Application/XXX...XXX/

Repackage the IPA:

mkdir Payload

cp -r someapp.app Payload

zip -r repackaged.ipa Payload

rm -rf Payload

On your Kali Linux, download the repackaged IPA:

scp root@192.168.1.10:/private/var/containers/Bundle/Application/XXX...XXX/repackaged.ipa ./

If you want to pull a decrypted IPA from your iOS device, see section Pull a Decrypted IPA.

10. Miscellaneous

Monitor the System Log

On your Kali Linux, run the following command:

idevicesyslog -p 1337

Monitor File Changes

SSH to your iOS device, then, download and run Filemon:

wget http://www.newosxbook.com/tools/filemon.tgz && tar zxvf filemon.tgz && chmod 777 filemon

./filemon -c -f com.someapp.dev

Always look for created or cached files, images/screenshots, etc. Use nano to edit files directly on your iOS device.

Sensitive files such as know your customer (KYC) and similar, should not persists in the app specific directories on the user device after the file upload.

Images/screenshots path:

cd /var/mobile/Containers/Data/Application/YYY...YYY/Library/SplashBoard/Snapshots

Dump the Pasteboard

After copying sensitive data, the app should wipe the pasteboard after a short period of time.

SSH to your iOS device and run the following commands:

cycript -p 1337

[UIPasteboard generalPasteboard].items

Press CTRL + D to exit.

You can also monitor the pasteboard in Objection.

Get the Provisioning Profile

scp root@192.168.1.10:/private/var/containers/Bundle/Application/XXX...XXX/*.app/embedded.mobileprovision ./

openssl smime -inform der -verify -noverify -in embedded.mobileprovision

11. Tips and Security Best Practices

Bypass any keyboard restriction by copying and pasting data into an input field.

Access tokens should be short lived, and if possible, invalidated on logout.

Don't forget to test widgets, push notifications, app extensions, and Firebase.


App should not disclose sensitive data in the predictive text (due to incorrectly defined input field type), app switcher, and push notifications.

App should warn a user when taking a screenshot of sensitive data, as well as, that it is trivial to bypass biometrics authentication if iOS device is jailbroken.

Production app (i.e. build) should never be debuggable.

12. Useful Websites and Tools

iMazing

Export IPA using iMazing (free trial). Jailbreak is not required.

iMazing

Figure 4 - iMazing