
iOS App Analysis
Static and dynamic analysis of iOS applications, working with IPA files, decrypting App Store binaries, inspecting the sandbox, and tooling around the iOS keychain.
Getting the IPA
Three sources, in order of how often I use them:
- The client provides a debug or ad-hoc IPA, easiest case
- Pull a decrypted IPA off a jailbroken device with
frida-ios-dumporbagbak - Sideload an IPA from IPA Library or similar archive sites for research
App Store binaries are encrypted with FairPlay. You cannot reverse the binary directly, you need to dump a decrypted copy from a running process.
# frida-ios-dump (works on most jailbreaks)
git clone https://github.com/AloneMonkey/frida-ios-dump
cd frida-ios-dump
pip install -r requirements.txt
# SSH must be configured to the device
./dump.py com.target.app
# Outputs target.ipaWhat's Inside an IPA
unzip target.ipa -d target/
# target/Payload/Target.app/
# Target <- the Mach-O binary
# Info.plist
# embedded.mobileprovision
# _CodeSignature/
# en.lproj/
# ... assets, frameworks, pluginsThe interesting bits:
Target, the Mach-O binary, what you actually disassembleInfo.plist, app metadata, permissions, URL schemes, ATS settingsembedded.mobileprovision, entitlements and provisioning infoFrameworks/, third-party libraries
First Pass: Info.plist
# Convert binary plist to XML
plutil -convert xml1 target/Payload/Target.app/Info.plist -o /tmp/info.xml
cat /tmp/info.xmlWhat I look for:
NSAppTransportSecurity, look forNSAllowsArbitraryLoads, cleartext exceptionsCFBundleURLTypes, registered URL schemes (deep link surface)UIFileSharingEnabled, exposes the Documents directory over iTunesNSCameraUsageDescription,NSLocationUsageDescription, etc., maps the privacy surfaceLSApplicationQueriesSchemes, schemes the app probes for, often hints at what it talks to
Entitlements
codesign -d --entitlements :- target/Payload/Target.app/TargetLook for:
keychain-access-groups, shared keychain accesscom.apple.security.application-groups, shared containersaps-environment, push environmentget-task-allow, debuggable build
get-task-allow set to true on a production build is a finding worth raising, it means the binary is debuggable.
Static Analysis
Hopper, Ghidra, IDA
For full disassembly. Hopper is the cheapest of the three and works well on iOS. Ghidra is free and good. IDA is the gold standard if you can afford it.
What I look at first in the disassembly:
- Methods on classes named
*Login*,*Auth*,*Token*,*Crypto* - String references to API endpoints
- Calls to
SecItemAdd,SecItemCopyMatching(keychain operations) - Calls to
NSURLSessionandURLSessionDelegatemethods (TLS pinning) NSUserDefaultsreads and writes- Anti-debug calls,
ptrace,sysctl,task_get_exception_ports
class-dump
class-dump -H target/Payload/Target.app/Target -o headers/
ls headers/Gives you Objective-C class headers. Useless for Swift binaries (Swift symbols are mangled), useful for ObjC.
Strings
strings -a target/Payload/Target.app/Target | grep -i "api\|key\|secret\|http"The bar for finding API URLs and hardcoded secrets is very low.
Dynamic Analysis
Connect to the device
# After installing OpenSSH from Cydia/Sileo
ssh root@<device-ip>
# Password: alpine (CHANGE IT IMMEDIATELY)Inspect the sandbox
The app's container is at /var/mobile/Containers/Data/Application/<UUID>/. Finding the right UUID is annoying, so use a tool:
# On the jailbroken device
which find-app # objection or similar
# Or with Objection from your laptop
objection -g com.target.app explore
> env
# Documents: /var/mobile/Containers/Data/Application/<UUID>/Documents
# Library: ...
# tmp: ...Inspect the keychain
The keychain is per-app (or per access group). Dump everything the app can see:
objection -g com.target.app explore
> ios keychain dump
> ios keychain dump --json /tmp/keychain.jsonNSUserDefaults
> ios nsuserdefaults getApps love to stash auth state and feature flags here. Sometimes including refresh tokens.
Cookies
> ios cookies getWatch a method
> ios hooking watch method "-[LoginViewController checkPinIsValid:]" --dump-args --dump-return --dump-backtraceConsole.app and idevicesyslog
Stream the device's system log. Apps log far more than they should.
# From macOS, with libimobiledevice installed
idevicesyslog | grep -i targetCommon Findings
| Finding | Where I find it |
|---|---|
| Hardcoded API keys | strings, Hopper string view |
get-task-allow=true in production | codesign -d --entitlements :- |
| Cleartext exceptions in ATS | Info.plist, NSAppTransportSecurity |
| Tokens in NSUserDefaults | Objection, ios nsuserdefaults get |
| Unprotected keychain items | ios keychain dump |
| Deep link bypasses | URL schemes in Info.plist, then test handlers |
| UIWebView (deprecated, vulnerable) | class-dump for UIWebView references |
| Logging tokens to system log | idevicesyslog, Console.app |
Bypassing Detections
iOS apps frequently check for:
- Jailbreak (cydia URL scheme,
/Applications/Cydia.app, write tests, suspicious dylib loads) - Debugger (
ptrace,sysctl, exception ports) - Hooking (Frida loaded modules, frida-server presence)
For the lazy implementations, Objection's ios jailbreak disable handles the common patterns. For custom checks, write a Frida hook against the specific function. Use frida-trace to find what gets called early in the app lifecycle.
Related Notes
Last updated on
Frida
Frida is the dynamic instrumentation toolkit I use on almost every mobile engagement. This is a working reference for installation, attaching to processes, writing hooks, and the patterns I reach for most.
Mobile Pentesting Fundamentals
Setting up a mobile pentesting environment for Android and iOS, choosing physical devices versus emulators, and the methodology I follow on every mobile engagement.