This is more of a checklist for myself. May contain useful tips and tricks. Still need to add a lot of things.
Everything was tested on Kali Linux v2023.1 (64-bit) and iPhone 7 with iOS v13.4.1 and unc0ver jailbreak v8.0.2.
For help with any of the tools type <tool_name> [-h | -hh | --help]
or man <tool_name>
.
If you didn't already, read OWAS MASTG (GitHub) and OWASP MASVS (GitHub). You can download OWASP MASTG checklist from here.
I also recommend reading Hacking iOS Applications and HackTricks - iOS Pentesting.
In most cases, to be eligible for a bug bounty reward, you need to exploit a vulnerability with non-root priviledges, possibly building your own "malicious" app.
Websites that you should use while writing the report:
- cwe.mitre.org/data
- owasp.org/projects
- owasp.org/www-project-mobile-top-10
- cheatsheetseries.owasp.org
- first.org/cvss/calculator/4.0
- nvd.nist.gov/vuln-metrics/cvss/v3-calculator
- nvd.nist.gov/ncp/repository
- attack.mitre.org
My other cheat sheets:
Future plans:
- install Burp Proxy and ZAP certificates,
- test widgets, push notifications, app extensions, and Firebase,
- deeplink hijacking,
- WebView attacks,
- disassemble, reverse engineer, and resign an IPA,
- future downgrades using SHSH BLOBS.
- Jailbreaking an iOS Device
- Cydia Sources and Tools
- SSL Kill Switch 2
- Kali Linux Tools
- Mobile Security Framework (MobSF)
1. Basics
3. Search for Files and Directories
5. Deeplinks
6. Frida
7. Objection
10. Tips and Security Best Practices
Jailbreaking an iOS device will void its warranty. I have no liability over your actions.
Jailbreak your iOS device using AltStore and unc0ver jailbreak.
Follow AltStore Docs to install AltStore on your PC.
[Optional] Fix the sideloading issue when installing AltStore on your iOS device. You can also use AltStore to install many other cool apps.
On your iOS device, open Safari, go to unc0ver.dev, and press on Open in AltStore
. Make sure your antivirus is disabled because it will flag unc0ver IPA as a malware and delete it from your PC.
Figure 1 - unc0ver Official Website
Open unc0ver, open the settings in the top-left corner, select it as in the image below, and run the jailbreak.
Figure 2 - unc0ver Jailbreak
If you don't mind sending logs to China, you can also try using 3uTools, it is very easy to use.
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 for v16+ installation issue)
- nano
- PreferenceLoader
- ReProvision Reborn
- SSL Kill Switch 2 (iOS 13)
- SQLite 3.x
- wget
- zip
Over time, some apps might start throwing errors due to the new updates, if reinstalling them using Cydian does not solve the issues, then try to uninstall them completely and install them again.
The following project is the original SSL Kill Switch 2 project which is discontinued and not supported on devices with iOS v13 and grater. To download the most up-to-date project, check the julioverne.github.io repository in Cydia.
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
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 nuclei radare2 sqlite3 sqlitebrowser xmlstarlet
pip3 install frida-tools objection property-lister file-scraper
More information about my tools can be found at ivan-sincek/property-lister and ivan-sincek/file-scraper.
Make sure that Frida and Objection are always up to date:
pip3 install --upgrade frida-tools objection
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
Install an IPA:
ideviceinstaller -i someapp.ipa
Uninstall an IPA:
ideviceinstaller -U com.someapp.dev
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.
Figure 3 - Sideloading an IPA using ReProvision Reborn
If you have an Apple developer membership, you can code sign your apps for up to 1 year; otherwise, you might have to code sign them every now and then.
If you don't mind sending logs to China. Install an IPA using 3uTools desktop app. Jailbreak is required.
Figure 4 - Sideloading an IPA using 3uTools
ssh root@192.168.1.10
Default password is alpine
.
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.
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 9. Repackage an IPA and AnyTrans.
To unpack, e.g., someapp.ipa
or decrypted.ipa
(preferred), run:
unzip decrypted.ipa
You should now see the unpacked Payload
directory.
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, or directly from the IPA:
pip3 install -r requirements.txt
python3 app.py ios -i someapp
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 2>/dev/null | 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.
Export an IPA using AnyTrans desktop app. Excellent for iOS backups too.
Figure 5 - Download an IPA using AnyTrans
Figure 6 - Export an IPA using AnyTrans
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
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
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 cleared by calling removeAllCachedResponses().
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 the 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.
Search for hardcoded sensitive data:
rabin2 -zzzqq somefile | grep -Pi '[^\w\d\n]+(?: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\d]*(?:\"\ *\:|\ *\=).+'
rabin2 -zzzqq somefile | grep -Pi '[^\w\d\n]+(?:bug|comment|fix|issue|note|problem|to(?:\_|\ |)do|work)[^\w\d\n]+.+'
Extract URLs, deeplinks, IPs, etc.:
rabin2 -zzzqq somefile | grep -Po '\w+\:\/\/[\w\-\.\@\:\/\?\=\%\&\#]+' | 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})*(?:[a-zA-Z0-9\+\/]{4}|[a-zA-Z0-9\+\/]{3}\=|[a-zA-Z0-9\+\/]{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
Search for 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\n]+(?: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\d]*(?:\"\ *\:|\ *\=).+'; 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\n]+(?:bug|comment|fix|issue|note|problem|to(?:\_|\ |)do|work)[^\w\d\n]+.+'; 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})*(?:[a-zA-Z0-9\+\/]{4}|[a-zA-Z0-9\+\/]{3}\=|[a-zA-Z0-9\+\/]{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
Automate all of the above file inspection (and more) with a single tool, also using multithreading.
apt-get -y install radare2
pip3 install file-scraper
file-scraper -dir Payload -o results.html -e default
More about my other project at ivan-sincek/file-scraper.
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.
Figure 7 - 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
Property Lister will dump all databases in plain-text automatically.
Unpack, e.g., someapp.ipa
or decrypted.ipa (preferred).
Dump all the databases, and extract and convert all the property list files inside an IPA:
property-lister -db Payload -o results_db
property-lister -pl Payload -o results_pl
Repeat the same for the app specific directories.
Download mobile Nuclei templates:
git clone https://github.com/optiv/mobile-nuclei-templates ~/mobile-nuclei-templates
Unpack, e.g., someapp.ipa
or decrypted.ipa (preferred).
Search for hardcoded sensitive data:
echo Payload | nuclei -t ~/mobile-nuclei-templates/Keys/ -o nuclei_keys_results.txt
cat nuclei_keys_results.txt | grep -Po '(?<=\]\ ).+' | sort -uf > nuclei_keys_results_sorted.txt
Get your iOS device UDID:
idevice_id -l
Create a backup:
idevicebackup2 backup --full -u $(idevice_id -l) ./backup
App should not backup any sensitive data.
Restore from a backup:
idevicebackup2 restore -u $(idevice_id -l) ./backup
Browse backups using iExplorer (demo) for Windows OS. There are many other iOS backup tools, but they cannot browse app specific directories.
iExplorer's default directory for storing iOS backups:
C:\Users\%USERNAME%\AppData\Roaming\Apple Computer\MobileSync\Backup\
You can place your backups in either this directory or change it in settings.
Figure 8 - iExplorer
Figure 9 - Browse a backup using iExplorer
Test /.well-known/apple-app-site-association using branch.io/resources/aasa-validator.
Sometimes, deeplinks can bypass authentication, including biometrics.
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 -a "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 -a "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-deeplink-fuzzing script with Frida:
frida -U -no-pause -l ios-deeplink-fuzzing.js -f com.someapp.dev
frida -U -no-pause --codeshare ivan-sincek/ios-deeplink-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.
Useful resources:
- frida.re
- learnfrida.info
- codeshare.frida.re
- dweinstein/awesome-frida
- interference-security/frida-scripts
- m0bilesecurity/Frida-Mobile-Scripts
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*'
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
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.
In case Objection detaches from the app, use the process ID to attach it back without restarting the app.
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.
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.
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.
On your Kali Linux, run the following command:
idevicesyslog -p 1337
Or, get the PID from a keyword:
keyword="keyword"; idevicesyslog -p $(frida-ps -Uai | grep -i "${keyword}" | tr -s '[:blank:]' ' ' | cut -d ' ' -f 1)
SSH to your iOS device, then, download and run Filemon:
wget http://www.newosxbook.com/tools/filemon.tgz && tar zxvf filemon.tgz && chmod +x 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. Sensitive files should not be stored in /tmp/
directory nor similar system-wide directories.
Images and screenshots path:
cd /var/mobile/Containers/Data/Application/YYY...YYY/Library/SplashBoard/Snapshots/
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.
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
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.
Sometimes, deeplinks and widgets can bypass authentication, including biometrics.
Only if explicitly allowed, try flooding 3rd party APIs to cause possible monetary damage to the company, or denial-of-service (DoS) by exhausting the allowed quotas/limits.
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.
App should warn a user that it is trivial to bypass biometrics authentication if iOS device is jailbroken.
Production app (i.e., build) should not be debuggable.
URL | Description |
---|---|
developer.apple.com/account | Official iOS documentation, create code signing certificates, etc. |
developer.apple.com/apple-pay/sandbox-testing | Test debit/credit cards for Apple Pay. |
streaak/keyhacks | Validate various API keys. |
zxing.org/w/decode.jspx | Decode QR codes. |
youtube.com/user/iDeviceMovies | Useful videos about jailbreaking, etc. |
ipsw.me/product/iPhone | Firmwares for Apple devices. |