SKILL: iOS Pentesting Tricks — Expert Attack Playbook
AI LOAD INSTRUCTION: Expert iOS application security testing techniques. Covers jailbreak vs non-jailbreak methodology, keychain extraction, URL scheme/Universal Links abuse, Frida/Objection runtime hooks, binary protection checks, and data storage analysis. Base models miss protection class nuances and AASA misconfiguration patterns.
0. RELATED ROUTING
Before going deep, consider loading:
Advanced Reference
Also load IOS_RUNTIME_TRICKS.md when you need:
- Frida recipes for iOS-specific hooks (ObjC class enumeration, method swizzling)
- Objection command reference for iOS
- Runtime hooking patterns and bypass templates
1. JAILBREAK VS NON-JAILBREAK TESTING
| Capability |
Jailbroken |
Non-Jailbroken |
| SSL pinning bypass |
Frida, SSL Kill Switch 2, Objection |
Network debugging proxy, MITM profiles (limited) |
| Keychain access |
keychain-dumper, Frida dump |
Only via backup extraction (limited) |
| Filesystem inspection |
Full access to app sandbox |
Only via ideviceinstaller + backup |
| Runtime manipulation |
Frida, Cycript, LLDB attach |
Frida on sideloaded apps (re-signed) |
| Binary analysis |
Class-dump, Hopper on-device |
Decrypt IPA on Mac, analyze offline |
| Method hooking |
Full Frida/Cycript capability |
Limited (needs re-signed app + Frida gadget) |
Non-Jailbreak Testing Setup
# Extract IPA from device
ideviceinstaller -l # List installed apps
ios-deploy --id <UDID> --download --bundle_id com.target.app
# Or use frida-ios-dump for decrypted IPA (jailbroken)
python dump.py com.target.app
# Sideload with Frida gadget (non-jailbreak runtime hooking)
# 1. Extract IPA, 2. Insert FridaGadget.dylib into Frameworks/
# 3. Re-sign with valid profile, 4. Install via ios-deploy
2. KEYCHAIN EXTRACTION
2.1 Keychain Protection Classes
| Protection Class |
Availability |
Use Case |
Risk Level |
kSecAttrAccessibleWhenUnlocked |
Only when device unlocked |
Passwords, tokens |
Medium |
kSecAttrAccessibleAfterFirstUnlock |
After first unlock until reboot |
Background tokens |
High (persists across locks) |
kSecAttrAccessibleAlways |
Always (deprecated iOS 12+) |
Legacy apps |
Critical |
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly |
Passcode set + unlocked |
High-value secrets |
Low |
2.2 Extraction Methods
# Jailbroken: keychain-dumper
/path/to/keychain-dumper -a # Dump all accessible items
/path/to/keychain-dumper -g password # Generic passwords only
/path/to/keychain-dumper -i # Internet passwords
# Frida / Objection
objection -g com.target.app explore
> ios keychain dump
> ios keychain dump --json # JSON output for parsing
# Frida script for keychain enumeration
frida -U -f com.target.app -l keychain_dump.js
2.3 What to Look For
| Item Type |
Keychain Class |
Typical Content |
kSecClassGenericPassword |
genp |
App tokens, API keys, user credentials |
kSecClassInternetPassword |
inet |
HTTP auth credentials, OAuth tokens |
kSecClassCertificate |
cert |
Client certificates |
kSecClassIdentity |
idnt |
Cert + private key pair |
kSecClassKey |
keys |
Encryption keys |
3. URL SCHEME HIJACKING
3.1 Custom URL Scheme Discovery
# From IPA/app bundle — check Info.plist
plutil -p /path/to/Payload/Target.app/Info.plist | grep -A 10 CFBundleURLTypes
# Example output:
# "CFBundleURLSchemes" => ["targetapp", "fb123456789"]
3.2 Hijacking Attack
Scenario: Target app registers "targetapp://" for OAuth callback
1. Attacker app also registers "targetapp://" URL scheme
2. User initiates OAuth login in target app
3. OAuth provider redirects to targetapp://callback?code=AUTH_CODE
4. iOS may open attacker's app instead (non-deterministic scheme resolution)
5. Attacker captures OAuth authorization code
| Attack Vector |
Technique |
Impact |
| OAuth callback interception |
Register same scheme |
Steal authorization codes |
| Deep link hijacking |
Register same scheme |
Phishing, data interception |
| Payment callback interception |
Register payment scheme |
Transaction manipulation |
3.3 URL Scheme vs Universal Links Security
| Feature |
Custom URL Scheme |
Universal Links |
| Registration |
Any app can claim any scheme |
Requires AASA file on domain |
| Uniqueness |
Not guaranteed (multiple apps) |
One app per domain path |
| Validation |
None |
Cryptographic (AASA signed) |
| Recommended for |
Non-sensitive navigation |
OAuth callbacks, sensitive actions |
| Hijackable |
Yes (duplicate registration) |
Only via AASA misconfiguration |
4. UNIVERSAL LINKS EXPLOITATION
4.1 AASA (Apple-App-Site-Association) Misconfiguration
# Fetch AASA file
curl -s "https://target.com/.well-known/apple-app-site-association" | jq .
curl -s "https://target.com/apple-app-site-association" | jq .
# Check for wildcard patterns (overly broad)
# Bad: "paths": ["*"] ← captures ALL URLs
# Bad: "paths": ["/NOT *"] ← poorly written exclusion
| Misconfiguration |
Risk |
Exploitation |
Wildcard paths (*) |
App claims all URLs on domain |
Redirect chain may break UL → fallback to URL scheme |
| Missing AASA file |
Universal Links won't work |
App falls back to less-secure URL scheme |
| AASA on wrong domain |
Links not associated |
Scheme hijacking possible |
AASA not served as application/json |
Parsing failure |
Links won't associate |
| CDN caching stale AASA |
Outdated associations |
Inconsistent behavior |
4.2 Breaking Universal Links → URL Scheme Fallback
Technique: Force Universal Link to not open app, causing fallback to URL scheme
1. User long-presses link → "Open in Safari" (disables UL for that domain)
2. Redirect chain: domain A → domain B → target (UL breaks on redirect)
3. JavaScript redirect instead of 302 (UL only works on server-side redirects)
4. App not installed → URL scheme fallback → hijackable
5. RUNTIME MANIPULATION
5.1 Frida on iOS
# Connect to app on jailbroken device
frida -U -f com.target.app --no-pause
# Basic ObjC exploration
> ObjC.classes # List all classes
> ObjC.classes.NSURLSession # Check if class exists
> ObjC.classes.AppDelegate.$methods # List methods
> ObjC.classes.AppDelegate['- isLoggedIn'].implementation # Read method
# Hook method and modify return value
Interceptor.attach(ObjC.classes.AuthManager['- isAuthenticated'].implementation, {
onLeave: function(retval) {
retval.replace(ptr(1)); // Force return TRUE
}
});
5.2 Objection iOS Commands
objection -g com.target.app explore
# Keychain
> ios keychain dump
# Cookies
> ios cookies get
# Pasteboard
> ios pasteboard monitor
# Jailbreak detection bypass
> ios jailbreak disable
# SSL pinning bypass
> ios sslpinning disable
# Binary info
> ios info binary
# Hooking
> ios hooking watch class AppDelegate
> ios hooking watch method "-[AuthManager isAuthenticated]" --dump-args --dump-return
> ios hooking set return_value "-[AuthManager isJailbroken]" false
5.3 Cycript (Legacy but Useful)
// Attach to running app
cycript -p com.target.app
// Explore UI hierarchy
UIApp.keyWindow.recursiveDescription().toString()
// Find view controllers
[UIWindow.keyWindow().rootViewController _printHierarchy].toString()
// Call methods directly
[AppDelegate.sharedInstance isLoggedIn] // → check return
AppDelegate.sharedInstance.isLoggedIn = true // → modify
// Access singleton instances
var vc = choose(LoginViewController)[0]
vc.bypassLogin()
6. BINARY PROTECTIONS
6.1 Checking Binary Security
# PIE (Position Independent Executable)
otool -hv /path/to/binary | grep PIE
# ARC (Automatic Reference Counting)
otool -I -v /path/to/binary | grep objc_release
# Stack canaries
otool -I -v /path/to/binary | grep __stack_chk_guard
# Encryption (FairPlay DRM)
otool -l /path/to/binary | grep -A 4 LC_ENCRYPTION_INFO
# cryptid 0 = decrypted, cryptid 1 = encrypted
| Protection |
Check |
Missing Impact |
| PIE |
MH_PIE flag in header |
ASLR disabled → predictable addresses |
| ARC |
_objc_release symbol |
Use-after-free more likely |
| Stack Canaries |
__stack_chk_guard |
Buffer overflow exploitation easier |
| Encryption |
cryptid value |
Binary readable without decryption |
6.2 Decrypting IPA
# frida-ios-dump (preferred, jailbroken device)
python dump.py com.target.app
# Outputs: decrypted IPA in current directory
# bagbak (alternative)
bagbak com.target.app
# Manual via Frida
frida -U -f com.target.app -l dump_memory.js
# Dump decrypted binary from memory, replace encrypted section
6.3 Class-dump for ObjC Analysis
# Dump Objective-C class information
class-dump /path/to/decrypted/binary > classes.h
class-dump -H /path/to/decrypted/binary -o /tmp/headers/
# Search for interesting patterns
grep -r "password\|token\|secret\|apiKey\|isJailbroken\|isRooted" /tmp/headers/
7. DATA STORAGE ISSUES
7.1 Sensitive Data Locations
| Location |
Path |
What to Check |
| NSUserDefaults |
Library/Preferences/<bundle-id>.plist |
Tokens, user data, feature flags |
| Core Data (SQLite) |
Library/Application Support/*.sqlite |
Cached API responses, user records |
| Keychain |
System keychain database |
Credentials, keys (check protection class) |
| Cookies |
Library/Cookies/Cookies.binarycookies |
Session cookies |
| Cache |
Library/Caches/ |
Cached API responses, images with PII |
| Screenshots |
Library/SplashBoard/Snapshots/ |
App state captured on background |
| Keyboard cache |
Library/Keyboard/ |
Autocomplete entries with sensitive input |
| Pasteboard |
System pasteboard |
Copied passwords, tokens |
| WebView storage |
Library/WebKit/WebsiteData/ |
LocalStorage, IndexedDB, cookies |
7.2 Inspection Commands
# On jailbroken device — app sandbox at:
# /var/mobile/Containers/Data/Application/<UUID>/
# Find app UUID
find /var/mobile/Containers -name "com.target.app" 2>/dev/null
# Check NSUserDefaults
plutil -p Library/Preferences/com.target.app.plist
# Check SQLite databases
sqlite3 Library/Application\ Support/Model.sqlite ".tables"
sqlite3 Library/Application\ Support/Model.sqlite "SELECT * FROM ZUSER;"
# Check for sensitive strings in all files
grep -r "password\|token\|bearer\|api_key" Documents/ Library/
8. TRANSPORT SECURITY (ATS)
8.1 ATS Exception Patterns
<!-- Info.plist — check for ATS exceptions -->
<key>NSAppTransportSecurity</key>
<dict>
<!-- WORST: disables ATS entirely -->
<key>NSAllowsArbitraryLoads</key>
<true/>
<!-- BAD: specific domain exceptions -->
<key>NSExceptionDomains</key>
<dict>
<key>insecure-api.target.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSExceptionMinimumTLSVersion</key>
<string>TLSv1.0</string>
</dict>
</dict>
</dict>
| ATS Setting |
Risk |
Notes |
NSAllowsArbitraryLoads = true |
Critical |
All HTTP allowed |
NSExceptionAllowsInsecureHTTPLoads |
High |
HTTP for specific domain |
NSExceptionMinimumTLSVersion = TLSv1.0 |
Medium |
Weak TLS |
NSAllowsArbitraryLoadsInWebContent |
Medium |
WebView can load HTTP |
| No ATS exceptions |
Low |
Proper configuration |
9. IOS PENTESTING DECISION TREE
Testing iOS application
│
├── Device jailbroken?
│ ├── Yes → full testing capability
│ │ ├── Keychain dump → ios keychain dump (§2)
│ │ ├── Filesystem inspection → check all data storage (§7)
│ │ ├── Runtime hooks → Frida/Objection (§5)
│ │ └── Binary analysis → class-dump decrypted binary (§6)
│ └── No → limited testing
│ ├── Re-sign with Frida gadget for runtime access
│ ├── Backup extraction for data analysis
│ └── Network-level testing (proxy + SSL bypass)
│
├── SSL pinning blocking proxy?
│ └── Yes → see mobile-ssl-pinning-bypass SKILL.md
│
├── URL schemes registered?
│ ├── OAuth callback via URL scheme? → hijacking risk (§3.2)
│ ├── Universal Links configured? → check AASA (§4.1)
│ └── Scheme used for sensitive actions? → test interception
│
├── Binary protections adequate?
│ ├── Missing PIE? → ASLR disabled (§6.1)
│ ├── No stack canaries? → overflow risk (§6.1)
│ └── Still encrypted? → decrypt first (§6.2)
│
├── Data storage secure?
│ ├── Tokens in NSUserDefaults? → plaintext extraction (§7.1)
│ ├── Keychain protection class? → AfterFirstUnlock = risky (§2.1)
│ ├── Screenshots captured? → check Snapshots dir (§7.1)
│ └── Keyboard cache? → check for sensitive autocomplete (§7.1)
│
├── ATS configured?
│ ├── ArbitraryLoads = true? → HTTP downgrade possible (§8)
│ └── Domain exceptions? → targeted HTTP interception
│
└── Runtime manipulation needed?
├── Jailbreak detection blocking? → ios jailbreak disable (§5.2)
├── Need to bypass auth check? → hook + modify return (§5.1)
└── Need to trace API calls? → method hooking (§5.2)