A comprehensive guide to managing iOS devices, installing and debugging apps, and performing penetration testing using various tools and commands.
- Listing and Managing Devices
- Booting and Installing on Simulator
- SSH into iOS Device
- Obtaining Device UDID
- Dumping IPA from Jailbroken Device
- Installing and Debugging iPhone Apps from Command Line
- Bypassing Protections
- Frida Usage
- Objection Usage
- Miscellaneous Tools and Commands
- Tips and Security Best Practices
- Useful Websites and Tools
- Example: iOS Hooking and Runtime Manipulation with DVIA-v2
xcrun xctrace list devices
open -a simulator
xcrun simctl boot <device_udid>
xcrun simctl install booted <path_to_app>
ssh [email protected] -o "StrictHostKeyChecking=no" \
-o "UserKnownHostsFile=/dev/null" \
-o "ProxyCommand=inetcat 44"
Password: alpine
idevice_id -l
ioreg -p IOUSB -l | grep "USB Serial"
Example Output:
"USB Serial Number" = "9e8ada44246cee813e2f8c1407520bf2f84849ec"
-
Install
ideviceinstaller
:brew install ideviceinstaller
-
List Device UDIDs:
idevice_id -l
Example Output:
316f01bd160932d2bf2f95f1f142bc29b1c62dbc
system_profiler SPUSBDataType | \
sed -n -e '/iPad/,/Serial/p;/iPhone/,/Serial/p;/iPod/,/Serial/p' | \
grep "Serial Number:"
Example Output:
Serial Number: 64655621de6ef5e56a874d63f1e1bdd14f7103b1
instruments -s devices
During development, apps are sometimes provided to testers via over-the-air (OTA) distribution. You'll receive an itms-services
link, such as:
itms-services://?action=download-manifest&url=https://s3-ap-southeast-1.amazonaws.com/test-uat/manifest.plist
-
Install the Tool:
npm install -g itms-services
-
Download the IPA File:
itms-services -u "itms-services://?action=download-manifest&url=https://s3-ap-southeast-1.amazonaws.com/test-uat/manifest.plist" -o - > out.ipa
Note: Start port forwarding.
iproxy 2222 44
Way 1:
python3 dump.py -l
python3 dump.py <APPLICATION_NAME> -u mobile -P alpine
Way 2:
frida-ps -Uai
# OR
python3 dump.py -l
python3 dump.py <Identifier>
-
Install
ios-deploy
:brew install ios-deploy
-
Install App:
Way 1:
ios-deploy -b <APP.ipa>
Way 2: Using TrollStore from Sileo
- Install TrollHelper.
- Install TrollStore after opening TrollHelper.
- Import unsigned IPA using the
+
button. - Installed and working!
Way 3: On a Rootful Jailbreak with Cydia
- Ensure AppSync is installed.
- Use Cydia directly to install the IPA or use tools like 3uTools for Mac.
ios jailbreak disable --quiet
Using Objection:
objection -g com.someapp.dev explore --startup-command 'ios jailbreak disable --quiet'
ios sslpinning disable --quiet
Using Objection:
objection -g com.someapp.dev explore --startup-command 'ios sslpinning disable --quiet'
ios ui biometrics_bypass --quiet
Using Objection:
objection -g com.someapp.dev explore --startup-command 'ios ui biometrics_bypass --quiet'
- Frida Official Website
- Learn Frida
- Frida Codeshare
- Awesome Frida by dweinstein
- Interference Security Frida Scripts
- Frida-Mobile-Scripts by m0bilesecurity
frida-ps -Uai
Filter by Keyword:
frida-ps -Uai | grep -i 'keyword'
frida-ps -Uai | grep -i 'keyword' | cut -d ' ' -f 1
frida-discover -U -f com.someapp.dev | tee frida_discover.txt
frida-trace -U -p 1337
With Specific Functions:
frida-trace -U -p 1337 -i 'recv*' -i 'send*'
frida -U -no-pause -l ios-touch-id-bypass.js -f com.someapp.dev
Using Codeshare:
frida -U -no-pause --codeshare ivan-sincek/ios-touch-id-bypass -f com.someapp.dev
On Touch ID Prompt:
Press Cancel
.
Recommendation: Use the built-in method in Objection.
frida -U -no-pause -l ios-hook-classes-methods.js -f com.someapp.dev
Using Codeshare:
frida -U -no-pause --codeshare ivan-sincek/ios-hook-classes-methods -f com.someapp.dev
-
Explore the App:
objection -g com.someapp.dev explore
-
Run a Frida Script in Objection:
import somescript.js
Using Startup Script:
objection -g com.someapp.dev explore --startup-script somescript.js
-
Binary Information:
ios info binary
-
Plist Information:
ios plist cat Info.plist
-
Environment Variables:
env
-
HTTP Cookies:
ios cookies get
-
Dump Keychain, NSURLCredentialStorage, and NSUserDefaults:
ios keychain dump ios nsurlcredentialstorage dump ios nsuserdefaults get
-
Dump App's Memory to a File:
memory dump all mem.dmp
-
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 a Class or Method:
ios hooking watch class 'someclass'
With Arguments and Return Values:
ios hooking watch method '-[someclass somemethod]' --dump-args --dump-return
-
Change Method's Return Value:
ios hooking set return_value '-[someclass somemethod]' false
-
Monitor Crypto Libraries:
ios monitor crypto
-
Monitor the Pasteboard:
ios pasteboard monitor
Alternatively, dump the pasteboard using Cycript.
On Kali Linux:
idevicesyslog -p 1337
Get 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:
-
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
Notes:
- Always look for created or cached files, images/screenshots, etc.
- Use
nano
to edit files directly on your iOS device. - Sensitive files should not persist in app-specific directories or system-wide directories like
/tmp/
.
cd /var/mobile/Containers/Data/Application/YYY...YYY/Library/SplashBoard/Snapshots/
-
SSH to Your iOS Device:
-
Run Cycript:
cycript -p 1337
-
Access Pasteboard Items:
[UIPasteboard generalPasteboard].items
-
Exit Cycript: Press
CTRL + D
.
Alternatively: Monitor the pasteboard in Objection.
scp [email protected]:/private/var/containers/Bundle/Application/XXX...XXX/*.app/embedded.mobileprovision ./
Verify the Profile:
openssl smime -inform der -verify -noverify -in embedded.mobileprovision
-
Keyboard Restrictions:
- Bypass by copying and pasting data into an input field.
-
Access Tokens:
- Should be short-lived and invalidated on logout.
-
Testing:
- Test widgets, push notifications, app extensions, and Firebase.
- Deeplinks and widgets can bypass authentication, including biometrics.
-
API Security:
- Flooding 3rd party APIs can cause monetary damage or DoS by exhausting quotas/limits. Only perform such actions if explicitly allowed.
-
Data Disclosure:
- Apps should not disclose sensitive data in predictive text, app switcher, or push notifications.
- Warn users when taking screenshots of sensitive data.
- Inform users that biometrics authentication can be bypassed if the device is jailbroken.
-
Production Apps:
- Should not be debuggable.
-
Runtime Application Instruments for iOS:
- Previously Passionfruit: Grapefruit
-
AppInfoScanner:
-
iOS Penetration Testing Cheat Sheet:
-
OWASP Mobile Security Testing Guide (MASTG):
-
Property Listener:
-
Usage:
property-lister -db Cache.db -o plists
-
Convert Plist to Readable XML:
plistutil -f xml -i com.someapp.dev.plist
- After testing and logging out, download app-specific directories and inspect all files.
- Extract Base64 strings from property list files to find sensitive data.
- Use
rabin2
overstrings
to read Unicode characters.
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]+.+'
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
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
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
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
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
Note: Deeplinks can bypass authentication, including biometrics.
-
Create Directory:
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"
-
Serve the HTML Files:
python3 -m http.server 9000 --directory ios_deeplinks
Notes:
url_schemes.txt
: See section Info.plist.urls.txt
: See section Inspect Files.
frida -U -no-pause -l ios-deeplink-fuzzing.js -f com.someapp.dev
Using Codeshare:
frida -U -no-pause --codeshare ivan-sincek/ios-deeplink-fuzzing -f com.someapp.dev
Additional Instructions:
- Check the source code for more details.
- You can paste the entire source code directly into Frida and call methods as needed.
Demonstrates how to use Frida and Objection to reverse engineer and manipulate an iOS app (DVIA-v2
).
-
Start Frida Trace:
frida-trace -U -i "*login*" DVIA-v2
-
Interact with the App:
-
Enter values in login boxes.
-
Press the "Login Method 1" button.
-
Observe Swift function calls in the console:
18737 ms _T07DVIA_v240RuntimeManipulationDetailsViewControllerC18loginMethod1TappedyypF() 22787 ms _T07DVIA_v240RuntimeManipulationDetailsViewControllerC18loginMethod2TappedyypF()
-
-
Load Objection into DVIA-v2:
objection --gadget DVIA-v2 explore
-
Search for Relevant Classes:
ios hooking search classes login
- Example Output: 30 classes found.
-
Watch Specific Class:
ios hooking watch class LoginValidate
- Methods Observed:
+[LoginValidate isLoginValidated]
+[LoginValidate validateCode:viewController:]
- Methods Observed:
-
Trigger Method Call:
-
Press "Login Method 1" again.
-
Console Output:
(agent) [442ynywbod3] Called: [LoginValidate isLoginValidated] (Kind: class) (Super: NSObject)
-
-
Watch Method with Arguments and Return Values:
ios hooking watch method "+[LoginValidate isLoginValidated]" --dump-args --dump-return
-
Console Output:
(agent) [p2itp5paimj] Called: +[LoginValidate isLoginValidated] 0 arguments(Kind: class) (Super: NSObject) (agent) [p2itp5paimj] Return Value: 0x0
-
-
Override Method Return Value:
ios hooking set return_value "+[LoginValidate isLoginValidated]" true
-
Result: Bypass the login screen.
-
Console Output:
(agent) [3so3hzb0hbz] +[LoginValidate isLoginValidated] Return value was: 0x0, overriding to 0x1
-
-
Create a Target Using App's Path:
(lldb) target create /private/var/containers/Bundle/Application/6561F7CA-CC6B-44EE-80FA-497BF19C57BA/DVIA-v2.app/DVIA-v2
Or Using Bundle Identifier:
(lldb) target create --ios com.highaltitudehacks.DVIAswiftv2
-
Launch the App:
(lldb) process launch
- Note: May take longer than expected. If stuck, press
Ctrl+C
.
- Note: May take longer than expected. If stuck, press
-
Calculate ASLR Offset:
-
Dump Image Sections:
image dump sections DVIA-v2
Or Run the Target:
(lldb) target run
-
-
Attach to Running Process by PID (Optional):
(lldb) process attach --pid <PID>
-
Set Breakpoints and Inspect Variables:
-
Example:
(lldb) breakpoint set --name <method_name>
-
-
Calculate Offset:
0x000000010260c000 - 0x0000000100000000 = 0xfc6adf00
-
Disassemble Function with Offset:
dis -s 0x2f4000+0x00000001001bd300
-
Set Breakpoint on Specific Address:
br set -a 0x1004b1314
-
Modify Register Value to Bypass Logic:
register write x0 0x1
-
Resume Execution:
(lldb) c
- Result: Success message on the application.
-
Search for Custom Files:
find . \( -name '*.txt' -o -name '*.json' -o -name '*.pdf' -o -name '*.doc' -o -name '*.docx' -o -name '*.ppt' -o -name '*.xls' -o -name '*.xlsx' \) 2>/dev/null
-
Search for Custom Databases:
find . \( -name '*.sqlite' -o -name '*.sqlite3' -o -name '*.db' -o \) 2>/dev/null
idevice_id --list
idevicesyslog -u <ID> | grep <APP_BUNDLE_ID>
-
Property Listener: ivan-sincek/property-lister
-
Usage After Downloading
Cache.db
:property-lister -db Cache.db -o plists
-
-
Convert Plist to Readable XML:
plistutil -f xml -i com.someapp.dev.plist
-
Inspect Files:
-
Extract Base64 Strings:
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
-
Decode Base64 Strings:
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
-