Welcome — this module builds a practical, deep foundation for iOS mobile pentesting. We’ll cover how iOS apps are structured, what to look for during initial analysis, and the core runtime & storage primitives you’ll encounter when moving to static and dynamic analysis. Everything here is safe, practical, and intended to prepare you for hands-on work in an authorized lab.
Learning objectives
By the end of this module you will be able to:
- Explain core iOS architecture concepts relevant to security (sandboxing, entitlements, code signing).
- Locate and inspect key files inside an
.ipa/.appbundle and interpret their meaning. - Identify common artifacts: Info.plist entries, embedded provisioning profiles, entitlements, and URL schemes.
- Understand Mach-O binary basics and the high-level differences between Objective-C and Swift artifacts.
- Locate where apps store sensitive data (UserDefaults, files, databases, Keychain) and the implications for security testing.
- Prepare a checklist of the first static checks to run on any iOS app during reconnaissance.
Prerequisites
- macOS host with Xcode command line tools installed (or a Linux host with relevant tools for some steps).
- Basic knowledge of macOS terminal, file system, and basic programming concepts (functions, classes).
- Access to an app binary for which you have written authorization (IPA or extracted
.appdirectory). - Recommended: an isolated lab network and a disposable device or simulator for testing.
Tooling (recommended)
You don’t need all of these at once — pick what fits your workflow. Tools are grouped by purpose.
Inspection & static analysis
unzip,tar(extract IPA)plutil(plist inspection)codesign(verify signatures)security(Certificate / provisioning profile inspection)otool,nm,strings,ldid(binary inspection)class-dump,class-dump-swift(Objective-C headers reconstruction)hopper,ghidra,ida,binary-ninja(disassembly / decompilation)mobSF(Mobile Security Framework) — automated static scanning
Runtime & dynamic basics (later modules)
frida,objection,lldb,cycript(for dynamic/runtime)
Storage inspection
sqlite3(DB analysis)plutil/defaults(plist and user defaults)KeychainDumptools (only in authorized lab environment / jailbroken devices)
Misc
jq(JSON formatting),openssl(cert checks),curl(API testing)
High-level iOS architecture (security-relevant)
Sandboxing
- Every iOS app runs inside a sandbox: a filesystem namespace and entitlements-limited environment.
- Sandbox prevents arbitrary access to other apps’ data and many system resources.
- Security implication: Misconfigured entitlements or shared app groups can create cross-app risks.
Code signing & provisioning
- Apple requires binaries to be signed. The code signature binds executable code with certificates and provisioning profiles that describe allowed entitlements and devices.
- Embedded provisioning profile (
embedded.mobileprovision) contains team IDs, entitlements, and device UDIDs (for ad-hoc builds). - Security implication: Understanding signing helps when repackaging, or when analyzing enterprise/ad-hoc builds that may leak certificates or test endpoints.
Entitlements
- Entitlements are key/value capabilities that alter app privileges (e.g.,
aps-environment,com.apple.developer.associated-domains,keychain-access-groups). - Many vulnerabilities arise from overly-broad entitlements (e.g., shared keychain access groups).
App lifecycle & processes
- Apps launch, go background/foreground, and are subject to lifecycle events (didFinishLaunching, applicationWillResignActive, etc.). Hooks in these places are common for auth/session handling and for placing security checks (or mistakes).
Packaging: IPA and .app
IPA structure
An .ipa is just a ZIP archive with a Payload/ folder. Inside Payload/YourApp.app/ you’ll find:
YourApp(Mach-O executable binary)Info.plist(app metadata)embedded.mobileprovision(provisioning)Assets.car,Assets.xcassets, images- Resource files, frameworks (
Frameworks/),PlugIns/, app extensions
Quick extraction
unzip AppName.ipa -d AppName_extracted
ls AppName_extracted/Payload/YourApp.app
Info.plist (what to inspect)
Info.plist is XML/JSON-like metadata. Security-relevant keys:
CFBundleIdentifier— app bundle identifier (used in many auth flows)CFBundleURLTypes/CFBundleURLSchemes— custom URL schemes (deep links, potential open-URL attacks)NSAppTransportSecurity— ATS exceptions that may allow insecure HTTPUIBackgroundModes— background capabilities (networking, voip)LSApplicationQueriesSchemes— other apps the app queries (inter-app comms)CFBundleShortVersionString,CFBundleVersion— versioning (useful for known CVEs)
Inspect:
plutil -p Payload/YourApp.app/Info.plist
Provisioning profile & certificates
- The
embedded.mobileprovisioncan be decoded to readable XML/JSON. It contains: developer team, app identifier patterns, allowed devices, entitlements, and certificates. - Check for:
Entitlementssection (overlaps with Info.plist entitlements)- Any leftover test team IDs or developer emails (may leak internal info)
application-identifierandkeychain-access-groups
Inspect:
security cms -D -i Payload/YourApp.app/embedded.mobileprovision > provision.plist
plutil -p provision.plist
Mach-O binary basics
The main executable in the .app is a Mach-O file (possibly a fat/universal binary). Important concepts:
- Architectures: arm64, armv7, x86_64 (simulator). Production apps for iPhones will have ARM slices.
- Load commands:
otool -lshows segments, load paths, and linked libraries. - Symbol tables:
nmandotool -Ivhelp locate exported symbols and functions. - Objective-C metadata: Objective-C stores class and selector names in the binary — often readable as strings.
Useful commands:
file Payload/YourApp.app/YourApp
lipo -info Payload/YourApp.app/YourApp # checks architectures
otool -L Payload/YourApp.app/YourApp # linked libraries
nm -gj Payload/YourApp.app/YourApp # global symbols
strings Payload/YourApp.app/YourApp | less
Security notes:
- String and symbol leaks often reveal secrets (API endpoints, crypto keys, class names used for auth).
- Swift binaries are more mangled; use
swift-demangleor specialized tools to recover readable names.
Objective-C vs Swift artifacts
Objective-C
- Retains class names and method selectors in runtime metadata — easy to enumerate.
- Tools like
class-dumporobjc-runtimeinspection can reconstruct header-like declarations.
Example workflow:
class-dump -H Payload/YourApp.app/YourApp -o headers/
Swift
- Uses name mangling and richer metadata; newer Swift versions complicate decompilation.
class-dump-swift,swift-demangle, or decompilers (Ghidra/Hopper) can help.- Swift’s runtime metadata still exposes function names and types but in a different format.
Implication for testing: Objective-C apps often leak more immediately useful info; Swift apps require more effort but still reveal seeds of logic and endpoints.
App protections & where they appear
High-level protections you will see and where they live:
- Obfuscation / symbol stripping — usually at compilation time (binary has few symbols).
- Encryption / string encoding — strings appear scrambled in binary/resources, decoded at runtime.
- Detect anti-debug / anti-tamper checks — code in
didFinishLaunchingor in critical flows. - Certificate pinning — usually in network stack code (NSURLSession delegates, custom sockets).
- Keychain usage — calls to Keychain APIs; look for Keychain entitlement misuse.
You’ll detect these with static inspection (strings, class-dump) and confirm them via dynamic tests later.
iOS storage primitives (what to look for)
Understanding where apps persist data is crucial.
UserDefaults (NSUserDefaults)
- Simple key/value store for preferences.
- Often used (and sometimes abused) to store tokens or flags.
- Located at
Library/Preferences/<bundle-id>.plistin app container.
Inspect:
plutil -p Library/Preferences/<bundle-id>.plist
Files / Caches
Documents/,Library/Caches/,tmp/— may store files or exported data.- Unencrypted files or debug logs can leak secrets.
SQLite / Realm / Core Data
- Databases often located in
Library/Application Support/orDocuments/. - Use
sqlite3to open.sqlitefiles and list tables.
Keychain
- iOS Keychain is intended for secure storage; however, misuse (like weak access control or shared keychain groups) can expose secrets.
- Keychain entries are not trivially extractable on non-jailbroken devices — but on jailbroken devices or via misconfigured access groups, risk exists.
Keychain vs Local Storage checklist:
- Check for tokens stored in plaintext in files/plists.
- Check for use of Keychain; note keychain-access-groups and team IDs.
Networking stack & where to find endpoints
- API endpoints are often present in Info.plist, config files, or as constants in code.
- Look for typical networking classes:
NSURLSession,AFNetworking,Alamofire. - TLS/SSL settings: ATS exceptions in Info.plist may permit HTTP or weak TLS.
Search strings:
strings Payload/YourApp.app/YourApp | egrep -i 'https?://|api|auth|token|login|baseUrl'
Also inspect .plist or *.json config files inside the bundle for environment switches (dev/test/prod) and hardcoded keys.
Quick static checklist (first-pass on any app)
Run these checks immediately after extracting the IPA:
- Extract IPA and list contents.
plutil -p Info.plist— inspect URL schemes, ATS exceptions, version, bundle ID.security cms -D -i embedded.mobileprovision— extract entitlements and provisioning metadata.lipo -infoandotool -Lon binary — check architectures and linked libs.strings/grepfor endpoints, keys, and interesting patterns (password,secret,token,apikey,client_secret).class-dumpfor Objective-C headers (if available).- Run
mobSFstatic scan if you need an automated baseline. - Note suspicious files (e.g.,
.sqlite,.plistwith secrets, unminified JS, config files).
Hands-on lab exercises (safe, reproducible)
These are safe first exercises you can perform in your authorized lab.
Lab 1 — Extract & inspect IPA metadata
- Place
AppName.ipain your working directory. - Extract:
unzip AppName.ipa -d App_extracted cd App_extracted/Payload/YourApp.app - Inspect Info.plist:
plutil -p Info.plist | sed -n '1,200p' - Decode provisioning:
security cms -D -i embedded.mobileprovision > provision.plist plutil -p provision.plist | sed -n '1,200p' - Record findings: bundle id, URL schemes, ATS exceptions, entitlements.
Deliverable: Short note with these items and at least one screenshot of the plutil output.
Lab 2 — Binary architecture and strings reconnaissance
- Find the binary file:
file YourApp lipo -info YourApp - List linked libraries and load commands:
otool -L YourApp otool -l YourApp | sed -n '1,200p' - Search for obvious URLs/keys:
strings YourApp | egrep -i 'https?://|api|auth|token|key|secret' | head -n 50 - Try
class-dump(if Objective-C):class-dump -H YourApp -o headers/ ls headers | head
Deliverable: A small table with:
- architectures,
- 3-5 interesting strings (endpoints or suspicious keys),
- whether class-dump produced headers.
Lab 3 — Local storage check (if you have the app container)
If you can obtain the application container (simulator or device backup), inspect persistent storage:
- Search for database files:
find . -type f \( -name '*.sqlite' -o -name '*.db' -o -name '*.plist' \) -print - Open a SQLite DB:
sqlite3 path/to/file.db .tables sqlite3 path/to/file.db "SELECT name FROM sqlite_master WHERE type='table';" - Inspect plist files:
plutil -p path/to/file.plist
Deliverable: List of discovered storage files and notes on any plaintext-looking tokens.
What to capture as evidence in Module 1
- Screenshots of
Info.plistandembedded.mobileprovisionoutputs. stringsresults showing API endpoints or suspicious plaintext.lipo/otooloutputs showing architectures and linked frameworks.- Notes on any suspicious entitlements or ATS exceptions.
- A short threat-model note enumerating the most likely areas of concern (e.g., insecure storage, dev/test endpoints).
Common pitfalls & how to avoid them
- Assuming simulator behavior equals device behavior. The iOS simulator uses x86_64 binaries and a macOS environment — behavior (and access to Keychain) differs from ARM device. Always validate findings on the intended platform if possible.
- Overlooking resource files. Sometimes the juicy stuff is in
.plist,.json, or.jsassets rather than the binary. - Ignoring obfuscation layers. Stripping symbols or obfuscation can make
class-dumpfail; don’t assume absence of strings means absence of logic — the app may decode strings at runtime. - Confusing provisioning for secrets. Embedded provisioning can contain team IDs and entitlements; it’s sensitive metadata but not a direct “secret key” usually.
Developer recommendations (what devs should do)
- Minimize sensitive data in plaintext within the bundle; use server-side secrets and short-lived tokens.
- Use Keychain for secrets and configure access groups tightly.
- Avoid ATS exceptions unless strictly necessary — and document the requirement.
- Strip debug endpoints and developer/test keys from production builds.
- Keep minimal entitlements and use associated domains or universal links rather than insecure custom URL schemes where possible.
Quick reference cheat sheet (commands)
# Extract IPA
unzip AppName.ipa -d App_extracted
# Inspect Info.plist
plutil -p App_extracted/Payload/YourApp.app/Info.plist
# Decode provisioning
security cms -D -i App_extracted/Payload/YourApp.app/embedded.mobileprovision > provision.plist
plutil -p provision.plist
# Binary info
file Payload/YourApp.app/YourApp
lipo -info Payload/YourApp.app/YourApp
otool -L Payload/YourApp.app/YourApp
strings Payload/YourApp.app/YourApp | egrep -i 'https?://|api|auth|token|key|secret' | head
# Generate Objective-C headers (if applicable)
class-dump -H Payload/YourApp.app/YourApp -o headers/
Next steps (where Module 1 prepares you to go)
- Module 2 (Reconnaissance & Information Gathering): leverage findings here to map backends, enumerate endpoints, and build a targeted test plan.
- Module 3 (Static Analysis): use the headers and string findings to do deeper reverse engineering and identify functions of interest.
- Module 4 (Dynamic Analysis): confirm static observations at runtime, inspect network flows, and begin controlled instrumentation.
