Imported from DragonJAR/iOS-Pentesting-Skill (
SKILL.md). Install upstream withnpx skills add DragonJAR/iOS-Pentesting-Skill. Copyright stays with the author (Apache-2.0).
iOS App Security Audit
Overview
Language Rule (CRITICAL): Match the user's language in your responses.
- If user speaks Spanish → reply in Spanish
- If user speaks English → reply in English
- If user speaks another language → reply in that language
- Default to English only when the user's language is unclear
This rule applies to ALL interactions: questions, explanations, findings, everything.
Deterministic 6-phase static analysis + optional dynamic confirmation workflow. Remove noise early, keep the bundle scope tight. Only report vulnerabilities where source, propagation, and sink are understood or clearly marked as needing dynamic confirmation.
When to Use
- User provides an IPA file for security review
- User asks to analyze decompiled iOS source code
- User needs to modify or repackage an IPA for testing
- User wants iOS mobile security testing or vulnerability assessment
- User needs help testing URL schemes, universal links, or IPC mechanisms
- User mentions iOS reverse engineering or app analysis
- User wants to acquire an IPA from the App Store (via ipatool — see references/ipa-extraction-guide.md §5)
New to iOS pentesting? Start with references/getting-started-ios.md for a 15-minute quick start guide. Stuck on an issue? See references/troubleshooting-ios.md for centralized troubleshooting. Quick workflow reference? See references/ios-pentesting-decision-tree.md for a visual decision tree of the complete testing workflow with environment options (jailbroken device, iOS Simulator, non-jailbreak).
Critical Rules
- NEVER report bare grep hits without traced context
- ALWAYS constrain searches to the app namespace (avoid framework noise)
- STOP and report immediately if decoding fails
- ALWAYS use imperative language in findings
- NEVER skip validation — if unsure, mark as "Needs Dynamic Confirmation"
- ALWAYS provide concrete PoC (frida script, objection command, or URL scheme payload)
- NEVER duplicate findings for the same root cause
Quality Note: Take your time to analyze thoroughly. Quality is more important than speed. Do not skip validation steps — a false positive is worse than a missed finding.
Reference: Before diving into specific analysis phases, consult
references/for detailed methodology on Mach-O analysis, crypto patterns, IPC abuse, and troubleshooting.
Interactive Decision Points
ALWAYS ask these questions BEFORE proceeding with the corresponding action.
Session Context (Remember for Entire Session)
The FIRST question establishes session context — remember the answer and reuse it.
When the user first provides an IPA or requests any action, establish:
- Target Environment — iOS Simulator OR Physical Device
- If Physical: Jailbreak status (jailbroken / non-jailbroken)
- If Simulator: iOS version/runtime
Store this context and reuse it for ALL subsequent actions in the session.
Rule: Only ask "Simulator or Physical Device?" ONCE per session. After the first answer, use that context for all install/hook/modify decisions without re-asking.
SimForge Requirement (Apple Silicon only): When Simulator is chosen, ALWAYS use SimForge to patch arm64 binaries for simulator compatibility. See
references/simforge-ios-testing.md.Example Flow:
- User: "Install this IPA"
- Agent: "Simulator or physical device?"
- User: "Simulator"
- Agent: [Uses SimForge to patch, then installs to simulator]
- ... later ...
- User: "Bypass SSL pinning"
- Agent: [Uses simulator commands directly, NO re-ask]
When Installing an IPA
User request: "Install this IPA", "Install the app", "Deploy the IPA"
Required questions:
- Target environment? — iOS Simulator OR Physical Device (jailbroken/non-jailbroken)
- If Physical Device: Is it jailbroken? (determines if Frida server is available)
- If Simulator: Which iOS version/runtime? (xcrun simctl list)
Commands based on answer:
- Simulator: Use SimForge to patch arm64 binary →
xcrun simctl install <UDID> <app-path>See
references/simforge-ios-testing.mdfor complete SimForge workflow - Physical (jailbroken):
ideviceinstaller install <ipa> - Physical (non-jailbroken): Needs re-signing with provisioning profile first
When Dynamic Analysis is Requested
User request: "Hook this app", "Bypass SSL pinning", "Intercept traffic", "Runtime analysis"
Required questions:
Target environment?— Use session context (established above)- Is Frida server running? — Verify with
frida-ps -U(device) orfrida-ps -D(simulator) - App bundle ID or process name? — Need exact identifier for attachment
When Modifying/Repackaging an IPA
User request: "Modify this IPA", "Patch the app", "Re-sign", "Repackage"
Required questions:
- What modification? — Bypass protection, patch code, change resources, etc.
Target device type?— Use session context (jailbroken vs stock)- Provisioning profile available? — Required if stock device
When URL Scheme Testing
User request: "Test URL schemes", "Test deep links", "IPC testing"
Required questions:
- Test target? — Static analysis (plist grep) OR Runtime (use session context)
- Specific schemes to test? — Or test all found in Info.plist
- Payload injection needed? — Yes for XSS/parameter testing, No for enumeration only
When Crypto/Keychain Testing
User request: "Test encryption", "Keychain analysis", "Crypto review"
Required questions:
- Analysis type? — Static (code patterns) OR Runtime (Frida hooks)
- Target algorithms? — CommonCrypto, CryptoKit, third-party, or all
- Key extraction needed? — Yes (requires Frida) or No (pattern analysis only)
Rule: NEVER assume the target environment. ALWAYS ask before executing install/hook/modify commands.
Execution Model
Tools to use: bash, write, edit, read, glob, grep for core workflow.
Toolchain Requirements
Before starting, verify these tools are installed:
- Frida (
frida --version) — pinned version in references/tool-versions.md; ⚠️frida-serveron device must match the hostfridacore version (frida --version) exactly - Objection 1.12.4 (
objection --version) — Note: Objection is in maintenance mode - dsdump dsdump_b2 (project archived Dec 2025) — Swift class dump for modern iOS binaries
- class-dump (optional) — Objective-C class dump for legacy apps
- plutil (system) — plist manipulation (preinstalled on macOS)
- codesign (system) — code signing (preinstalled on macOS)
- security (system) — keychain access (preinstalled on macOS)
- lipo (system) — fat binary manipulation (preinstalled on macOS)
- nm (system) — symbol extraction (preinstalled on macOS)
- strings (system) — string extraction (preinstalled on macOS)
- otool (system) — Mach-O analysis (preinstalled on macOS)
- jtool2 (optional) — Advanced Mach-O analysis
- SimForge (REQUIRED for arm64 apps on Simulator, Apple Silicon only) —
simforge— Patches Mach-OLC_BUILD_VERSIONto run arm64 iOS apps in the simulator with dylib injection (no jailbroken device required). Seereferences/simforge-ios-testing.md. - ipatool (optional) —
ipatool --version— Download IPAs from the App Store. Output is FairPlay-encrypted. See references/ipa-extraction-guide.md §5. - Python 3.10+ (
python3 --version)
Run scripts/preflight-check.sh (bash), scripts/preflight_check.py (universal, JSON output), or scripts/preflight-check.ps1 (PowerShell) to verify all tools. See references/environment-setup-ios.md for installation instructions. See references/tool-versions.md for version requirements.
Phase 0 — Extract and Detect Framework
Extract IPA
# Extract IPA (IPA is a zip file)
unzip app.ipa -d extracted/
cd extracted/Payload/YourApp.app/
Mach-O Analysis
# Analyze binary structure
otool -l YourApp | rg -A 10 "LC_ENCRYPTION_INFO(_64)?" # Check encryption (covers arm64)
lipo -info YourApp # Architectures (arm64, armv7, etc.)
file YourApp # Verify Mach-O type
dsdump YourApp > classdump-swift.txt # Dump Swift classes (if Swift)
class-dump YourApp > classdump-objc.txt # Dump ObjC classes (if available)
Reference:
references/macho-binary-analysis.mdfor the complete Mach-O analysis methodology (LC_ENCRYPTION_INFO, LC_CODE_SIGNATURE, segment layout, symbol tables, strip/binary thinning). Reference:references/advanced-tools-ios.mdfor the deep-dive tool set (Corellium, Hopper, Ghidra, frida-trace, lldb). Reference:references/bitcode-analysis.mdwhen the binary is bitcode-enabled (older App Store builds). Reference:references/tool-installation-ios.mdfor tool install and troubleshooting. Reference:references/libimobiledevice-guide.mdfor device pairing, syslog, backup, and filesystem access via the libimobiledevice toolchain. Encrypted binary recovery: ifcryptid == 1afterotool -l, use the bundledassets/frida-scripts/dump-ios.jsto dump the decrypted binary from memory.
Framework Detection
Identify the app's architecture early to tailor analysis.
| Framework | Detection Method | Key Artifacts |
|---|---|---|
| React Native | rg "RCTBridgeModule" · Check for libReactNative.a |
index.ios.bundle, libReactNative.a |
| Flutter | Check Frameworks/ for Flutter.framework and App.framework · Check for FLT* keys in Info.plist (FLTEnableImpeller, FLTEnableSurfaceControl) |
App.framework, Flutter.framework, flutter_assets/ |
| Cordova/Ionic | rg "cordova" in Info.plist · Check www/ directory |
cordova.js, config.xml |
| Xamarin | rg "xamarin" · Check for Mono runtime |
libmonosgen-2.0.dylib |
| Unity | rg "UnityEngine" · Check for UnityFramework.framework |
Data/Managed/, UnityFramework.framework |
| Swift Native | Check for Swift symbols · dsdump output shows Swift classes |
Swift-only codebase |
| Objective-C | Check for ObjC runtime · class-dump shows ObjC classes |
ObjC-only codebase |
Reference:
references/framework-detection-ios.mdfor complete detection scripts, Frida detection hooks, and framework-specific security considerations. Framework-specific deep dives (load the relevant one when you detect the framework):
references/react-native-ios-security.mdreferences/flutter-ios-security.mdreferences/cordova-ios-security.mdreferences/xamarin-ios-security.mdreferences/unity-ios-security.md(also covers running Unity IL2CPP games in the simulator — Metal heap fix — and IL2CPP reverse engineering with Il2CppDumper on macOS)references/swiftui-security-deep-dive.md(even for native SwiftUI apps)references/core-ml-security.mdandreferences/core-ml-security-expanded.md(apps with on-device ML)
Obfuscation Detection
- Swift Obfuscation: Generic class names (
a.b.c,x_1,internal_), mangled symbols - LLVM Obfuscator: Mixed naming, string encryption
- Custom Obfuscation: Unusual patterns, mixed naming schemes
- Third-Party Packers: iMAS, Arxan, GuardSquare (iXGuard), DexGuard iOS
Reference:
references/static-analysis-patterns-ios.mdfor detailed detection patterns. Reference:references/obfuscation-detection-ios.mdfor SwiftShield, IOSSecuritySuite, Arxan, and Irdeto detection methodology.
OPTIONAL: MobSF Integration
Reference:
references/mobsf-integration-guide.mdfor complete MobSF setup and API usage.
The iOS-Pentesting-Skill offers optional Mobile Security Framework (MobSF) integration for automated static and dynamic analysis.
Key Points:
- ✅ OPTIONAL: User choice, no hard dependency
- ✅ NON-BLOCKING: Works perfectly without MobSF
- ✅ ENHANCEMENT: MobSF results complement manual analysis
- ✅ CORRELATION: Findings merged with manual analysis
Interactive Setup (automatically prompts):
# Run MobSF detection and setup
python3 scripts/setup_mobsf.py
This script will:
- Check if MobSF is already installed
- Offer installation options if not found
- Configure API access
- Test connectivity
- Save configuration for future sessions
Quick Start (Docker):
# Install MobSF via Docker
./scripts/install-mobsf-docker.sh
# Configure API
python3 scripts/mobsf_config.py set-url http://127.0.0.1:8000
python3 scripts/mobsf_config.py set-key generate
# Verify configuration
python3 scripts/mobsf_config.py status
Using MobSF in Analysis:
# Upload IPA to MobSF
python3 scripts/mobsf_upload.py --ipa app.ipa
# Get scan results
python3 scripts/mobsf_export.py --scan-id <SCAN_ID>
# Export findings for correlation
python3 scripts/mobsf_export.py --scan-id <SCAN_ID> --format json
Alternative: Local Install (no Docker):
# Detect an existing MobSF install or a running server
python3 scripts/mobsf_detect.py
# Install MobSF locally (clones repo + sets up a virtualenv)
./scripts/install-mobsf-local.sh
# Start the local MobSF server
./scripts/run-mobsf-local.sh
Correlation: merge MobSF's automated detections with your manual source-to-sink findings to deduplicate and enrich evidence using scripts/mobsf_findings_correlator.py.
NOTE: MobSF integration is OPTIONAL. The skill works perfectly without it.
Phase 1 — Attack Surface Mapping
Analyze Info.plist
plutil -p Info.plist # Pretty print plist
# Extract specific keys (only works on top-level keys)
plutil -extract CFBundleURLTypes xml1 -o - Info.plist # CFBundleURLSchemes is nested inside this array
plutil -extract NSAppTransportSecurity xml1 -o - Info.plist
# For nested keys, use PlistBuddy:
/usr/libexec/PlistBuddy -c "Print :CFBundleURLTypes:0:CFBundleURLSchemes" Info.plist
URL Schemes (Deep Links)
Extract and document all custom URL schemes:
| Key | What It Controls | Security Impact |
|---|---|---|
| CFBundleURLSchemes | Custom schemes (e.g., myapp://) |
URL scheme injection via malicious URLs |
| CFBundleURLTypes | Scheme declarations | Attack surface enumeration |
| NSAppTransportSecurity | TLS/ATS settings | Cleartext traffic, insecure connections |
Info.plist Security Keys
| Key | What It Controls | Security Checks |
|---|---|---|
| UIBackgroundModes | Background capabilities | Unintended background data collection |
| NSPhotoLibraryUsageDescription | Photo access | Permission request clarity, photo library scope |
| NSCameraUsageDescription | Camera access | Permission request clarity |
| NSMicrophoneUsageDescription | Microphone access | Permission request clarity |
| NSLocationWhenInUseUsageDescription | Location access | Permission request clarity |
| NSLocationAlwaysAndWhenInUseUsageDescription | Background location | Justification for always-on tracking |
| NSFaceIDUsageDescription | Face ID biometric | Required since iOS 11; fallback policy |
| NSContactsUsageDescription | Contacts access | PII extraction scope |
| NSHealthShareUsageDescription | HealthKit read | PII / health data scope |
| NSHealthUpdateUsageDescription | HealthKit write | Health data write justification |
| NSRemindersUsageDescription | Reminders access | PII scope |
| NSBluetoothPeripheralUsageDescription | Bluetooth (legacy) | Legacy key, replaced in iOS 13 |
| NSBluetoothAlwaysUsageDescription | Bluetooth (modern) | iOS 13+ required for always-on BLE |
| NSSpeechRecognitionUsageDescription | Speech recognition | Audio data exfiltration scope |
| NSUserTrackingUsageDescription | App Tracking Transparency | Required for IDFA access since iOS 14.5 |
| NSAppTransportSecurity | ATS exceptions | See references/ats-configuration-guide.md |
| LSApplicationQueriesSchemes | canOpenURL whitelist |
Enumerates installed apps; surface for fingerprinting |
| ITSAppUsesNonExemptEncryption | Export compliance | Triggers App Store export compliance prompt |
| UIFileSharingEnabled | iTunes file sharing | Data exposure via iTunes / Files app |
| UISupportedExternalAccessoryProtocols | External accessories | Attack surface expansion |
| UIRequiresPersistentWiFi | WiFi requirement | Network behavior predictability |
| NSAppClipboardUsageDescription | Pasteboard read (UIPasteboard) | macOS Catalyst only; iOS doesn't require usage description — audit clipboard usage via static analysis |
Entitlements Analysis
# Extract entitlements from binary
codesign -d --entitlements - YourApp.app/YourApp
| Entitlement | Purpose | Security Impact |
|---|---|---|
| keychain-access-groups | Shared keychain access | Cross-app data sharing |
| get-task-allow | Debugger attachment | Debug mode in production — must be false in release |
| application-groups | App group containers | Shared data between apps / extensions |
| com.apple.developer.applesignin | "Sign in with Apple" capability | Required when offering third-party SSO |
| com.apple.developer.in-app-payments | Apple Pay capability | Payment flow integrity |
| com.apple.developer.icloud-container-identifiers | iCloud document storage | Data exfiltration via iCloud sync |
| com.apple.developer.icloud-services | iCloud (CloudKit, Key-Value) | CloudKit data scope |
| com.apple.developer.ubiquity-container-identifiers | iCloud ubiquity (Documents) | Document sync scope |
| com.apple.developer.associated-domains | Universal Links / Handoff | Deep link / Universal Link routing |
| com.apple.developer.default-data-protection | Default NSFileProtection class |
Determines at-rest encryption for new files |
| com.apple.developer.pass-type-identifiers | Wallet pass type | Pass authentication scope |
| com.apple.developer.healthkit | HealthKit access | Health data scope |
| com.apple.developer.homekit | HomeKit access | Smart home device control scope |
| aps-environment (production / development) | Push notifications | APNs token handling, dev vs prod routing |
| com.apple.developer.networking.wifi-info | WiFi network info (iOS 13+) | SSID/BSSID exposure; opt-in |
| com.apple.security.application-groups | App group containers (App Sandbox — macOS only) | N/A on iOS; do not expect in IPA entitlements |
| com.apple.security.network.client | Outbound network (App Sandbox — macOS only) | N/A on iOS; the iOS sandbox always allows outbound |
| com.apple.security.network.server | Inbound network (App Sandbox — macOS only) | N/A on iOS; iOS apps cannot bind listening TCP/UDP sockets on cellular or WiFi (sandbox restriction) |
App Extensions Detection
# List all app extensions
find . -name "*.appex" -type d
Common extensions:
- Today Widget:
TodayExtension.appex - Share Extension:
ShareExtension.appex - Action Extension:
ActionExtension.appex - Notification Content:
NotificationContentExtension.appex
Reference:
references/info-plist-checklist.md— complete Info.plist key checklist (47 keys). IPC / URL scheme abuse: seereferences/url-scheme-security-guide.mdandreferences/universal-links-guide.md. App extensions (Today / Share / WidgetKit / Notification Content / Keyboard):references/app-extensions-security.md. Privacy manifest (PrivacyInfo.xcprivacy, App Tracking Transparency):references/privacy-manifest-ios.mdandreferences/ios-privacy-testing.md. iOS version diffs (iOS 15 → 18 changes that affect attacks):references/ios-version-security-changes.md.
Enterprise & MDM Apps
In-house / VPP / DEP apps and MDM-managed devices expand the attack surface via configuration profiles, managed app config, and enterprise distribution certificates.
- Configuration profiles (
.mobileconfig): inspect for over-broad payloads, injected trusted CAs, and VPN/proxy settings. - Managed app config: read the
NSUserDefaultskeycom.apple.configuration.managed. - Enterprise certs: validate the signing identity and provisioning scope (
codesign -dvv).
Reference:
references/enterprise-app-testing.md,references/mdm-security-testing.md, andreferences/configuration-profile-abuse.md. Dynamic (Phase 4):assets/frida-scripts/mdm-bypass.js(MDM restriction bypass) andassets/frida-scripts/enterprise-cert-bypass.js(enterprise cert trust bypass). Automation:references/automation-scripts-ios.mdandscripts/auto-audit-static-ios.sh(one-shot static analysis driver for Phases 0–3).
Data Storage Locations (Quick Reference)
When analyzing data storage, check these locations for sensitive data exposure:
| Location | Path Pattern | What to Check | Tool |
|---|---|---|---|
| NSUserDefaults | Library/Preferences/*.plist |
Tokens, user data, feature flags, config | plutil -p |
| Core Data | Library/Application Support/*.sqlite |
Cached API responses, user records, local DB | sqlite3 |
| Keychain | System keychain database | Credentials, encryption keys, certificates | keychain-dumper, Frida |
| Cookies | Library/Cookies/*.binarycookies |
Session cookies, authentication tokens | Binary parsing |
| Cache | Library/Caches/ |
Cached API responses, images with PII | find, strings |
| Snapshots | Library/SplashBoard/Snapshots/ |
App state screenshots (may contain PII) | Image review |
| Keyboard | Library/Keyboard/ |
Autocomplete entries with sensitive input | strings |
| Pasteboard | System pasteboard | Copied passwords, tokens, sensitive data | Frida hook |
| WebView Storage | Library/WebKit/WebsiteData/ |
LocalStorage, IndexedDB, cookies | sqlite3 |
| Tmp | tmp/ |
Temporary files, may contain sensitive data | find, strings |
Full reference:
references/data-protection-api-guide.mdTesting: Useassets/frida-scripts/pasteboard-monitor.js,assets/frida-scripts/keylogger-basic.js
Phase 2 — Targeted Triage
Scoped Grep Patterns
ALWAYS grep within the app namespace only. Use patterns from references/static-analysis-patterns-ios.md:
| Category | Example Patterns (ObjC/Swift) | What to Look For |
|---|---|---|
| WebView sinks | loadRequest, loadHTMLString, evaluateJavaScript |
Loading untrusted URLs |
| IPC sources | openURL(_:), application(_:open:), continue(_:) |
Unsanitized data entry from URL schemes |
| Keychain | SecItemAdd, SecItemCopyMatching, kSecAttrAccessible |
Insecure storage, weak accessibility |
| UserDefaults | UserDefaults, standardUserDefaults, set |
Cleartext storage of sensitive data |
| Pasteboard | UIPasteboard.general, string, setItems |
Data exposure via clipboard |
| Hardcoded secrets | password.*=, api[_-]?key, "sk_live_, secret |
Credentials in code |
| Crypto | kCCAlgorithmAES, kCCOptionECBMode, MD5, SHA1 |
Weak algorithms, insecure modes |
| TLS/ATS | NSAllowsArbitraryLoads, NSExceptionDomains |
Disabled certificate validation |
| Logging | NSLog, print, os_log |
Sensitive data in logs |
| File I/O | writeToFile, FileManager, createFile |
Path traversal, insecure file ops |
| Background | beginBackgroundTask, BGTaskScheduler |
Unintended background data collection |
| Biometric | LocalAuthentication, evaluatePolicy |
Biometric bypass, fallback to PIN |
| SQLite | sqlite3_exec, sqlite3_prepare_v2 |
SQL injection |
| CoreData | NSPersistentContainer, execute |
Data query injection |
Binary Analysis Commands
# Extract strings from binary
strings YourApp | rg -i "(password|key|secret|token|api)"
# List exported symbols (ObjC) — -gU filters out undefined system symbols
nm -gU YourApp | rg " T "
# List dynamic libraries
otool -L YourApp
# Dump Objective-C class methods
class-dump YourApp
# Dump Swift classes (modern)
dsdump YourApp
# Check for specific symbols (e.g., crypto usage) — -gU to skip system library noise
nm -gU YourApp | rg -i "(AES|DES|MD5|SHA)"
Resource File Analysis
Check Info.plist and embedded plists for secrets:
rg -i "(key|token|secret|password|api)" Info.plist
rg -i "(key|token|secret|password|api)" -g "*.plist"
Reference:
references/static-analysis-patterns-ios.mdfor grep patterns organized by vulnerability type. Reference:references/objc-security-patterns.mdandreferences/swift-security-patterns.mdfor language-specific pitfalls (swizzling, KVO, type-safety, generics). Reference:references/runtime-hardening-checks-ios.mdfor the runtime hardening controls that are routinely missed: background snapshot obfuscation, custom keyboard risk, AutoFill, screen recording detection, URLSession audit, WKWebView cache cleanup, file protection classes, biometric replay protection,nscurlATS testing, Universal Links validation, and app group containers.
Phase 2 — Per-area reference files
Use these when a category from the table above lights up:
- Crypto / CommonCrypto →
references/commoncrypto-analysis.md - CryptoKit (Swift crypto) →
references/cryptokit-hook-ios.js - Keychain →
references/keychain-security-guide.md - UserDefaults →
references/userdefaults-security.md - CoreData →
references/coredata-security.md - UIPasteboard →
references/pasteboard-security.md - File protection / NSFileProtection* →
references/data-protection-api-guide.md - Deserialization security →
references/insecure-deserialization-ios.md - Passkeys / WebAuthn →
references/passkeys-security-ios.md - Swift Concurrency →
references/swift-concurrency-security.md - CloudKit / iCloud sync →
references/cloudkit-security-ios.md
Phase 2 — Runtime Frida scripts
Map the script to the finding category:
- Cryptography →
crypto-hook-ios.js - CryptoKit (Swift crypto) →
cryptokit-hook-ios.js - Keychain / Secure Enclave →
keychain-dumper.js,secure-enclave-probe.js - UserDefaults →
nsuserdefaults-dumper.js - CoreData →
coredata-interceptor.js - Pasteboard →
pasteboard-monitor.js,clipboard-exfiltration.js - File protection →
data-protection-check.js,file-protection-enumerator.js - ATS / cleartext →
app-transport-security.js - Network →
network-interceptor-ios.js - Network.framework (iOS 13+) →
nwconnection-hook-ios.js - Deserialization →
deserialization-hook-ios.js - Passkeys/WebAuthn (iOS 16+) →
passkeys-hook-ios.js - Swift Concurrency →
swift-concurrency-monitor-ios.js
SBOM & Supply Chain (OWASP M2)
Enumerate embedded frameworks, third-party SDKs, and their known CVEs:
# Generate a Software Bill of Materials from the IPA (CycloneDX / SPDX / JSON)
python3 scripts/sbom_generator.py --input app.ipa --format all --output sbom/
# Scan the SBOM for vulnerable or risky dependencies
python3 scripts/dependency_scanner.py --sbom sbom/sbom.json --check-cves --check-licenses --output dep-scan.json
Reference:
references/sbom-supply-chain-guide.mdfor SBOM formats (CycloneDX/SPDX) and supply-chain risk methodology;references/third-party-sdk-analysis.mdfor per-SDK review (trackers, network behavior, ATS exemptions). Runtime SDK behavior:assets/frida-scripts/sdk-tracer.jsenumerates loaded third-party SDKs and hooks their methods for actual network/credential behavior (often more accurate than the static SDK inventory). MobSF correlation (optional):scripts/mobsf_findings_correlator.pymerges your manual findings with MobSF's automated ones, deduplicating overlapping detections.
Phase 3 — Data Flow Tracing
Source-to-Sink Methodology
Map data flow from untrusted sources to dangerous sinks:
Common Sources (iOS)
| Source | Method | Example |
|---|---|---|
| URL Schemes | application(_:open:) |
Malicious scheme URLs |
| Universal Links | application(_:continueUserActivity:) |
Universal link abuse |
| Pasteboard | UIPasteboard.general.string |
Data from clipboard |
| App Groups | Shared container NSFileManager |
Inter-app data |
| XPC | NSXPCConnection |
Inter-process communication |
| Keychain | SecItemCopyMatching |
Stored credentials |
| Notifications | UNUserNotificationCenter |
Notification payloads |
| WebView JS Bridge | WKScriptMessageHandler |
Untrusted JS calls |
Common Sinks (iOS)
iOS scope: iOS apps run in a sandbox and cannot spawn child processes (no
NSTask, noProcess, nopopen). Realistic "execution" paths on iOS go through WebView JS bridges, XPC, URL schemes, or app group data exfiltration — not OS command execution.
| Sink | Method | Impact |
|---|---|---|
| WebView Load | WKWebView.load(_:), loadHTMLString(_:baseURL:), evaluateJavaScript(_:completionHandler:) |
XSS, Phishing, JS bridge abuse |
| WebView JS Bridge | WKScriptMessageHandler callbacks, WKScriptMessageHandlerWithReply, window.webkit.messageHandlers.* |
Native code execution via injected JS |
| File Operations | Data.write(to:), String.write(toFile:atomically:encoding:), FileManager.createFile(atPath:contents:attributes:) |
Path traversal, sandbox escape, LFI |
| XPC (ObjC) | NSXPCConnection.remoteObjectProxy method invocations, NSXPCListenerEndpoint |
IPC injection, privilege escalation |
| XPC (C-level) | xpc_connection_send_message, xpc_connection_send_barrier |
XPC message injection, low-level IPC abuse |
| openURL | UIApplication.open(_:options:completionHandler:) |
URL scheme/Universal Link injection, app-to-app attack |
| canOpenURL | UIApplication.canOpenURL(_:) (requires LSApplicationQueriesSchemes) |
Installed app enumeration |
| Keychain Write | SecItemAdd, SecItemUpdate, SecItemDelete |
Credential poisoning, keychain clearing |
| Pasteboard Write | UIPasteboard.general.setItems(_:options:) |
Clipboard exfiltration, data leakage to other apps |
| SQLite | sqlite3_exec, sqlite3_prepare_v2 + binding, FMDB.executeQuery |
SQL injection |
| CoreData | NSFetchRequest with attacker-influenced NSPredicate |
Predicate injection, data exfiltration |
| UserDefaults Write (Swift) | UserDefaults.standard.set(_:forKey:), UserDefaults.standard.set(_:forKey:) variants |
Config tampering, sensitive data persistence |
| UserDefaults Write (ObjC) | [[NSUserDefaults standardUserDefaults] setObject:forKey:], setBool:forKey:, setInteger:forKey: |
Config tampering, sensitive data persistence |
| Logging | NSLog, os_log, print |
Sensitive data in logs, log injection |
| Notifications | UNUserNotificationCenter.add(_:withCompletionHandler:), UNMutableNotificationContent |
Notification payload abuse, action hijacking |
| Biometric | LAContext.evaluatePolicy(_:localizedReason:reply:) |
Biometric bypass, fallback to device passcode |
Decision Rules
| Rule | Condition | Action |
|---|---|---|
| 1 | Direct flow source → sink | Report as Likely |
| 2 | Indirect flow via static analysis | Report as Likely if path clear |
| 3 | Dynamic/reflective call | Mark as Needs Dynamic Confirmation |
| 4 | Native boundary | Mark as Needs Dynamic Confirmation |
| 5 | Library code | Verify if app wraps securely |
| 6 | No sanitization | Escalate severity |
Manual Checks Grep Misses
| Check | Why grep misses | How to verify |
|---|---|---|
| Runtime permissions | requestAuthorization() calls |
Trace completion handlers |
| Custom entitlement protections | Entitlement checks in code | Look for permission checks |
| URL scheme handling | openURL(_:) hides parameter extraction |
Follow URL parsing and validation |
| Shared containers | App groups + shared paths | Map containerURL(forSecurityApplicationGroupIdentifier:) |
| XPC communication | Interface definitions + dynamic calls | Trace XPC connection setup |
| WebView JS Bridge | WKScriptMessageHandler + JS code |
Map message handlers to JS injection points |
Reference:
references/deep-link-exploitation-ios.mdfor modern attack vectors: URL scheme injection, universal link abuse, WebView XSS, pasteboard attacks, XPC injection, app group data leakage, notification abuse. Reference:references/url-scheme-security-guide.mdfor a complete scheme-by-scheme audit checklist (registration, parsing, validation, origin checks). Reference:references/ipc-xpc-guide.mdandreferences/xpc-services-security.mdfor the XPC connection lifecycle and audit patterns. Reference:references/webview-security-ios.mdfor WKWebView and UIWebView attack surface (uiwebview-interceptor.jsruntime hook). Reference:references/notification-security.mdfor APNS token handling and notification payload abuse. Reference:references/app-groups-security.mdfor shared container attack surface andapp-groups-inspector.jsruntime hook.
Phase 3 — Runtime Frida scripts
- URL scheme / openURL →
url-scheme-monitor.js,deeplink-security-analyzer.js,deep-link-fuzzer.js - XPC →
xpc-tracer.js - WebView →
uiwebview-interceptor.js - Push notifications →
notification-interceptor.js - App groups →
app-groups-inspector.js - BGTask / background URLSession →
background-fetch-monitor.js - Clipboard as exfiltration channel →
keylogger-basic.js(only when relevant)
Phase 4 — Dynamic Analysis (Optional)
Use when static analysis hits a wall: obfuscation, reflection, native code, runtime protections.
Simulator-Based Testing (Apple Silicon, no jailbreak required)
When no jailbroken device is available, SimForge lets you run a decrypted arm64 iOS app in the iOS simulator and inject dylibs (e.g., Frida Gadget) without frida-server. Use it as a fast first iteration before escalating to a physical device.
# 1. Convert the decrypted .app bundle to simulator platform (patches LC_BUILD_VERSION)
simforge convert /path/to/Payload/YourApp.app
# 2. Ad-hoc re-sign (convert breaks codesig)
codesign -f -s - /path/to/Payload/YourApp.app/Frameworks/*
codesign -f -s - /path/to/Payload/YourApp.app
# 3. Boot a simulator and install
xcrun simctl boot "iPhone 15"
xcrun simctl install booted /path/to/Payload/YourApp.app
# 4. Launch with dylib injection (Frida Gadget, tweak, etc.)
simforge launch --bundleid com.example.app --dylib /path/to/frida-gadget.dylib
# 5. Connect Frida to the Gadget's default port (27042)
frida -H 127.0.0.1:27042 -n Gadget -l assets/frida-scripts/ssl-pinning-bypass-ios.js
Prerequisite for HTTP(S) interception in the simulator: install your proxy's CA cert into the simulator's trust store before launching the app. On the simulator this is a single command (no Settings UI, no trust toggle), and the proxy host is
127.0.0.1— the simulator shares the host's loopback. Seereferences/simulator-mitm-setup.mdfor the full flow (works for Burp Suite, Charles, mitmproxy, Proxyman, OWASP ZAP).
Frida on iOS Simulator (Frida 17+): Use the
.implementationhook pattern and attach by PID. All simulator-specific gotchas, CLI syntax changes, and verified recipes are inreferences/frida-ios-simulator-workflow.md.Simulator visibility: By default, the simulator launches in visible mode. Only use headless mode when explicitly requested by the user.
Simulator or physical device?
- Simulator (Frida 17+):
references/frida-ios-simulator-workflow.md- Physical device (jailbroken):
references/frida-ios-guide.md
Reference:
references/simforge-ios-testing.mdfor the complete workflow, internal architecture (LC_BUILD_VERSION patching,SIMCTL_CHILD_DYLD_INSERT_LIBRARIESinjection, tmpfs overlays), use cases (Frida Gadget, Theos tweaks, URL scheme testing, SpringBoard patching), limitations, and troubleshooting. For deep dives, seereferences/simforge-emulation-flow.md(the canonical 10-step procedure with pre-flight checks),references/simforge-crash-triage.md(diagnostic methodology with decision tree bytermination.namespace+indicator), andreferences/simforge-compatibility.md(FairPlay / Swift ABI / hardware capability matrix). Unity games in the simulator (Metal heap crash): if a converted Unity (IL2CPP) game aborts at graphics init withMTLStorageModePrivate is required for heaps, inject the interposer dylibassets/dylibs/metalheapfix.m(one-command driver:scripts/unity_sim_metalfix.sh <UDID> <bundle-id>). Full methodology + IL2CPP reversing (Il2CppDumper on macOS) inreferences/unity-ios-security.md; triage entry inreferences/simforge-crash-triage.md.
Troubleshooting: Mixed Load Commands
If the app crashes immediately after simforge convert with dyld assertion or SIGSEGV:
# Detect the problem: frameworks with BOTH load commands
otool -l Payload/YourApp.app/Frameworks/Suspect.framework/Suspect | grep -A3 "LC_VERSION_MIN\|LC_BUILD"
# Bad output shows BOTH:
# cmd LC_VERSION_MIN_IPHONEOS
# version 10.0
# cmd LC_BUILD_VERSION
# platform 7
# Clean up using the provided script
python3 scripts/simforge_cleanup.py Payload/YourApp.app
# Re-sign
codesign -f -s - --all-architectures Payload/YourApp.app
# Relaunch
xcrun simctl launch booted com.example.app
Why this happens: SimForge adds LC_BUILD_VERSION for simulator but doesn't remove the legacy LC_VERSION_MIN_IPHONEOS. Modern dyld rejects binaries with mixed version commands. See references/simforge-legacy-loadcmd-cleanup.md for detailed methodology.
Unity IL2CPP Simulator Fix (metalheapfix)
Critical: Unity 2021.x IL2CPP apps crash on the iOS Simulator due to
MTLHeapusingMTLStorageModeShared(valid on device, rejected by simulator'sMTLSimDriver). This is NOT a code signing or packaging issue — it's a Metal driver incompatibility.
Symptom: App launches, immediately crashes with:
abort → __assert_rtn → Metal MTLReportFailure
MTLSimDriver -[MTLSimDevice newHeapWithDescriptor:]
UnityFramework MetalHeap::CreateHeap(MTLHeapDescriptor*)
Fix: A dylib that intercepts MTLCreateSystemDefaultDevice() via DYLD_INTERPOSE and swizzles -[MTLSimDevice newHeapWithDescriptor:] to return nil. This forces Unity to fall back to direct buffer allocation (device.newBufferWithLength:options:) which the simulator supports.
⚠️ WRONG APPROACH (don't repeat): Forcing
descriptor.storageMode = MTLStorageModePrivatepasses the heap assertion but crashes at buffer creation (MTLSimHeap newBufferWithLength:→ XPC error) because Unity's scratch buffers need CPU write access and Private heaps are GPU-only. The correct fix is returningnil, not forcing Private.
Source (metalheapfix.m):
#import <Foundation/Foundation.h>
#import <Metal/Metal.h>
#import <objc/runtime.h>
static id<MTLDevice> (*orig_MTLCreaterSystemDefaultDevice)(void);
static dispatch_once_t onceToken;
static id swizzled_newHeapWithDescriptor(id self, SEL _cmd, MTLHeapDescriptor *descriptor) {
NSLog(@"[metalheapfix] newHeapWithDescriptor: -> returning nil (force non-heap path)");
return nil;
}
static void installSwizzle(id<MTLDevice> device) {
dispatch_once(&onceToken, ^{
Class cls = object_getClass(device);
Method original = class_getInstanceMethod(cls, @selector(newHeapWithDescriptor:));
if (original) {
method_setImplementation(original, (IMP)swizzled_newHeapWithDescriptor);
NSLog(@"[metalheapfix] installed heap swizzle on %s", class_getName(cls));
}
});
}
__attribute__((used))
static const struct { const char* name; void* replacement; void* original; } interpose_MTLCreaterSystemDefaultDevice
__attribute__((section("__DATA,__interpose"))) = {
"MTLCreateSystemDefaultDevice",
(void*)new_MTLCreaterSystemDefaultDevice,
(void*)&orig_MTLCreaterSystemDefaultDevice
};
Compile on TARGET machine (do NOT copy dylib from another machine):
SDK=$(xcrun --sdk iphonesimulator --show-sdk-path)
clang -dynamiclib -fobjc-arc -Wno-incompatible-function-pointer-types \
-isysroot "$SDK" -target arm64-apple-ios15.0-simulator \
-framework Foundation -framework Metal \
-o metalheapfix.dylib metalheapfix.m
codesign -f -s - metalheapfix.dylib
Complete Unity Simulator Workflow:
# 1. SimForge convert (after extraction)
simforge convert /path/to/Payload/YourApp.app
# 2. Ad-hoc sign (simforge breaks codesig)
codesign -f -s - YourApp.app/Frameworks/*
codesign -f -s - YourApp.app
# 3. Verify signature
codesign -v --strict YourApp.app
# 4. Install on SIMULATOR (NOT --console-pty, it kills the app)
UDID=<your iOS 18+ simulator UDID>
xcrun simctl install $UDID YourApp.app
# 5. Launch with dylib injected (decoupled — stays alive)
SIMCTL_CHILD_DYLD_INSERT_LIBRARIES="/path/to/metalheapfix.dylib" \
xcrun simctl launch $UDID com.example.app
# 6. Verify
xcrun simctl spawn $UDID launchctl list | grep -i appname
xcrun simctl io $UDID screenshot screenshot.png
⚠️ Runtime version matters: Test on the SAME iOS runtime that was verified. iOS 18.6 works. iOS 17.5 may NOT work — the
MTLSimDriverbehavior differs between runtimes and the swizzle may not prevent heap creation on older runtimes. ALWAYS checkxcrun simctl list devicesand match the runtime explicitly.
Combine with Frida: You can inject multiple dylibs by colon-separating:
SIMCTL_CHILD_DYLD_INSERT_LIBRARIES="/path/to/metalheapfix.dylib:/path/to/frida-gadget.dylib"
Making it Permanent: Embedding the Dylib in the Bundle
The SIMCTL_CHILD_DYLD_INSERT_LIBRARIES approach only works per-launch via command line. To make the fix permanent (app launches by tapping the icon), embed the dylib inside the bundle:
Concept: Add an LC_LOAD_DYLIB load command to the Mach-O binary so dyld loads the dylib automatically at startup. This is the same technique used for Frida Gadget injection, bypass tweaks, and any permanent code modification.
┌─────────────────────────────────────┐
│ App.app/ │
│ ├── Binary (main executable) │ ← Add LC_LOAD_DYLIB here
│ ├── Frameworks/ │
│ │ ├── UnityFramework.framework/ │
│ │ ├── metalheapfix.dylib │ ← Copy dylib here
│ │ └── ... │
│ └── Info.plist │
└─────────────────────────────────────┘
Step-by-step:
# 1. Copy dylib into Frameworks/
cp metalheapfix.dylib App.app/Frameworks/
# 2. Set the install name (dylib's internal identity)
install_name_tool -id "@executable_path/Frameworks/metalheapfix.dylib" \
App.app/Frameworks/metalheapfix.dylib
# 3. Add LC_LOAD_DYLIB to the main binary
# install_name_tool CANNOT add new load commands — use one of these:
# Option A: LIEF (Python, most reliable, cross-platform)
pip install lief
python3 -c "
import lief
binary = lief.MachO.parse('App.app/YourBinary')
for b in binary.builders:
b.add(lief.MachO.DylibCommand.from_path('@executable_path/Frameworks/metalheapfix.dylib'))
b.write('App.app/YourBinary')
break
"
# Option B: insert_dylib (third-party)
insert_dylib --strip-codesig --all-yes \
@executable_path/Frameworks/metalheapfix.dylib App.app/YourBinary
# Option C: optool
optool install -c load -p @executable_path/Frameworks/metalheapfix.dylib \
-t App.app/YourBinary
# 4. Re-sign everything (order matters — dylib first, bundle last)
codesign -f -s - App.app/Frameworks/metalheapfix.dylib
codesign -f -s - App.app/Frameworks/*
codesign -f -s - App.app
codesign -v --strict App.app
# 5. Install and launch — NO DYLD_INSERT_LIBRARIES needed
xcrun simctl install $UDID App.app
xcrun simctl launch $UDID com.example.app # just works!
Unity note: The main binary is a thin stub (~85KB). Adding
LC_LOAD_DYLIBto the stub is sufficient — dyld processesDYLD_INTERPOSEglobally regardless of which binary triggered the load. If it doesn't work, also add the load command toUnityFramework.framework/UnityFramework.
This technique applies to ANY dylib injection: Frida Gadget, SSL pinning bypass, jailbreak detection bypass, custom tweaks — the process is identical.
Frida iOS Integration
Frida Installation on macOS (Python 3.14+): macOS with Homebrew Python 3.14+ has PEP 668 enabled, which blocks global pip installs. See
references/tool-installation-ios.mdfor complete troubleshooting.
# Recommended: Use pipx (isolated installation)
brew install pipx
pipx install frida-tools
export PATH="$HOME/.local/bin:$PATH"
# Alternative 1: --break-system-packages (dev/testing)
python3 -m pip install --break-system-packages frida frida-tools
# Alternative 2: User directory
python3 -m pip install --user frida frida-tools
export PATH="$HOME/Library/Python/3.14/bin:$PATH"
# Verify installation
frida --version # Should show 17.x
Simulator Workflow (Frida 17+)
# Step-by-step for iOS Simulator:
# 1. Get simulator UDID
UDID=$(xcrun simctl list devices booted | grep -oE "[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}")
# 2. Launch app
xcrun simctl launch booted com.example.app
sleep 2
# 3. Get PID (alternative method if simctl spawn fails)
PID=$(xcrun simctl spawn booted launchctl list 2>/dev/null | rg "com.example.app" | awk '{print $1}' || ps aux | rg "com.example.app" | awk '{print $2}')
# 4. Attach Frida (CRITICAL: Use -D for simulator, not -U)
frida -D $UDID -p $PID -l script.js
Syscall Hook Limitations: On simulator, C-level syscall hooks (
stat(),fopen(),fork(),posix_spawn()) may fail. Use ObjC method hooks via.implementationpattern instead. Seereferences/frida-ios-simulator-workflow.mdfor complete patterns and limitations.
Device Workflow (Jailbroken)
-U= USB device (jailbroken physical device with frida-server running). For simulator, use-D <SIMULATOR_UDID>instead — see Simulator Workflow below.Gotcha:
frida-ps -Uworks without jailbreak (uses lockdownd API), butfrida -U -p/frida -U -frequire jailbreak or Frida Gadget. If attach fails with "need Gadget to attach on jailed iOS", the device is not jailbroken — see references/troubleshooting-ios.md for solutions (SimForge, Gadget injection).
# Spawn app with hook
frida -U -f com.example.app -l script.js
# Attach to running process
frida -U com.example.app -l script.js
Frida for iOS
// Check if Frida can attach
if (ObjC.available) {
console.log("Objective-C runtime available");
// List all loaded classes
console.log(ObjC.classes);
} else {
console.log("Objective-C runtime not available");
}
Reference: bundled Frida scripts in
assets/frida-scripts/. Seereferences/frida-scripts-index.mdfor the canonical catalog. Focused runtime triage: usepasteboard-monitor.jsfor clipboard visibility,xpc-tracer.jsfor XPC/native boundary discovery, andurl-scheme-monitor.jsfor URL scheme logging.
Script Maturity Levels:
- STABLE: Production-ready (
ssl-pinning-bypass-ios.js,jailbreak-detection-bypass.js,biometric-bypass-ios.js,network-interceptor-ios.js, etc.)- BETA: Functional but incomplete — use with caution
Objection iOS Commands
objection -g com.example.app explore
# Enumerate components
ios hooking list classes
# Bypass protections
ios jailbreak disable
ios sslpinning disable
ios keychain dump
ios biometrics bypass
ios pasteboard monitor
ios userdefaults get
iOS Runtime Testing
# Test URL scheme
xcrun simctl openurl booted "scheme://host/path?param=value"
# Monitor logs (device)
idevicesyslog | rg com.example.app
# Monitor logs (simulator)
log stream --predicate 'process == "YourApp"' | rg com.example.app
Test URL Scheme Payload
# Example: malicious URL scheme payload
myapp://deeplink?param=<script>alert(1)</script>
SSL Pinning bypass:
references/ssl-pinning-bypass-ios.md+assets/frida-scripts/ssl-pinning-bypass-ios.jsAnti-tamper bypass:references/runtime-integrity-bypass.md+assets/frida-scripts/jailbreak-detection-bypass.js(comprehensive: ObjC file/URL/write-test checks +stat/fopen/fork/posix_spawn/dlopensyscalls + directisJailbroken/jailbreakTestmethod discovery) for anti-debug, anti-frida, and jailbreak detection bypass. For a quick, low-noise pass on apps that only callisJailbroken-style methods (or when the C-level hooks destabilize the target), use the lightweightassets/frida-scripts/jailbreak-bypass-ios.jsinstead. For simulator work, preferjailbreak-bypass-ios.js— it uses the.implementationpattern required by Frida 17+ on the simulator (seereferences/frida-ios-simulator-workflow.md). RASP bypass:
- Comprehensive guide:
references/rasp-guide-ios.md+assets/frida-scripts/rasp-bypass-ios.js— 15 RASP solutions (FreeRASP, IOSSecuritySuite, JailMonkey, plus 12 commercial: Arxan/Digital.ai, Guardsquare, Promon SHIELD, Appdome, Irdeto, WhiteCryption, Verimatrix, Zimperium, IBM Trusteer, Pradeo, V-Key, Build38). Includes detection methods, weaknesses, and bypass techniques for each.- Reusable patterns:
references/rasp-patterns-ios.md— Generic RASP bypass patterns (dylib injection, SimForge complete conversion, framework stub creation, Frida 17+ simulator patterns, white screen diagnosis). Applicable to any iOS app.- Framework stub guide:
references/rasp-framework-stub-guide.md— How to create Objective-C framework stubs for simulator compatibility (NuDetectSDK, IOSSecuritySuite, FreeRASP templates included) Debugger detection:references/debugger-detection-bypass.mdforsysctl P_TRACED,ptrace PT_DENY_ATTACH,getppid()checks and Frida Interceptor bypasses. Secure Enclave probing:references/secure-enclave-testing.md+assets/frida-scripts/secure-enclave-probe.js. iCloud Private Relay testing:references/icloud-private-relay-testing.mdfor relay-aware network captures. Enterprise / MDM bypass:assets/frida-scripts/mdm-bypass.jsandassets/frida-scripts/enterprise-cert-bypass.jsfor managed-device restrictions and enterprise certificate trust. Jailbreak setup:references/jailbreak-setup-guide.mdfor the supported iOS versions and current checkra1n / palera1n / Dopamine coverage. Frida server installation:references/frida-server-ios-install.mdfor step-by-step Frida server setup on jailbroken devices. Frida on iOS (full guide):references/frida-ios-guide.mdfor installation, gadget mode, spawn vs attach. Objection (full guide):references/objection-ios-guide.mdfor non-Frida interactive exploration. Reference:references/libimobiledevice-guide.mdfor device pairing, syslog, backup, screenshot, and IPA installation commands.
Phase 5 — Classification and Reporting
Confidence Levels
| Level | Definition | Example Evidence |
|---|---|---|
| Confirmed | Full source-to-sink trace validated | Direct call chain from URL scheme source to WKWebView.evaluateJavaScript() with no sanitization |
| Likely | Strong evidence, minor gaps | Static trace clear but reflection obscures final sink |
| Needs Dynamic Confirmation | Static analysis inconclusive | Obfuscated code or native boundary requiring runtime verification |
Severity
Use CVSS 4.0. See references/cvss-scoring-ios.md for complete methodology, iOS-specific examples, and severity mapping. See references/cvss-examples-ios.md for 10 worked examples (ATS bypass, keychain exposure, URL scheme hijack, jailbreak bypass, biometric bypass, etc.).
Finding Template
## [ID] - [Title]
**Confidence**: [Confirmed/Likely/Needs Dynamic Confirmation]
**Severity**: [Critical/High/Medium/Low] (CVSS: [X.X])
**CWE**: [CWE-ID]
**OWASP**: [OWASP Category]
### Description
[1-2 sentences explaining what the vulnerability is]
### Affected Components
- **File**: `path/to/file.m` or `path/to/ViewController.swift`
- **Method**: `methodName` or `@objc func methodName()`
- **Component**: `[ViewController/Service/Extension]` (if applicable)
### Attack Scenario
1. Attacker [action, e.g., crafts malicious URL scheme with payload]
2. App [processing step, e.g., extracts parameter without validation]
3. Data propagates through [call chain]
4. Reaches sink [dangerous operation]
5. Results in [impact, e.g., arbitrary JavaScript execution in WebView context, data exfiltration, or credential theft]
### Proof of Concept
```bash
# Example: URL scheme path traversal
xcrun simctl openurl booted "myapp://deeplink?path=../../../../etc/passwd"
```
Or provide Frida hook script for dynamic verification:
```javascript
// Hook vulnerable method
const targetClass = ObjC.classes.MyViewController;
if (targetClass) {
const method = targetClass['- processURL:'];
if (method) {
Interceptor.attach(method.implementation, {
onEnter: function(args) {
console.log("URL processed: " + ObjC.Object(args[2]));
}
});
}
}
```
### Impact
- **Confidentiality**: [High/Medium/Low/None] — [explanation]
- **Integrity**: [High/Medium/Low/None] — [explanation]
- **Availability**: [High/Medium/Low/None] — [explanation]
### Remediation
```swift
// Provide secure code example
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
// Parse URL query parameters safely using URLComponents
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let queryItems = components.queryItems,
let input = queryItems.first(where: { $0.name == "path" })?.value,
isValidPath(input) else {
os_log("Invalid path detected")
return false
}
loadContent(at: input)
return true
}
private func isValidPath(_ path: String) -> Bool {
// Prevent path traversal and validate format
return !path.contains("..") && path.range(of: "^/safe/[a-zA-Z0-9_]+\\.html$", options: .regularExpression) != nil
}
```
Or Objective-C:
```objc
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options {
// NSURL has no -queryParameterValue: method. Use NSURLComponents + NSURLQueryItem.
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
if (!components) return NO; // Invalid URL
NSArray<NSURLQueryItem *> *queryItems = components.queryItems;
NSString *input = nil;
for (NSURLQueryItem *item in queryItems) {
if ([item.name isEqualToString:@"path"]) {
input = item.value;
break;
}
}
if ([self isValidPath:input]) {
[self loadContentAtPath:input];
} else {
NSLog(@"Invalid path detected");
return NO;
}
return YES;
}
```
### CVSS 4.0 Calculation
[Show vector string and score breakdown]
Coverage Statement
End your report with:
Coverage Analysis:
- Static Analysis: Complete (all decompiled sources analyzed)
- Dynamic Analysis: [Complete/Partial/Not Performed] (reason if partial)
- Scope: [com.example.app.* bundle only]
- Framework: [React Native/Flutter/Native/Standard]
- Obfuscation: [Swift Obfuscator/Custom/None]
Limitations:
- [List any limitations, e.g., "Native code analysis requires additional tools"]
- [Any components that could not be analyzed]
- [Any findings requiring additional verification]
Total Findings: X (Critical: Y, High: Z, Medium: A, Low: B)
Reference:
references/reporting-templates-ios.mdfor executive summary format, remediation priority matrix, and presentation templates.
Automated Report Generation
Use the generate-report.py script to generate professional HTML or Markdown reports from findings JSON:
# Generate HTML report
python3 scripts/generate-report.py \
--input findings.json \
--output report.html \
--app-name "My App" \
--bundle-id "com.example.app"
# Generate Markdown report
python3 scripts/generate-report.py \
--input findings.json \
--output report.md \
--app-name "My App" \
--bundle-id "com.example.app"
The script supports both JSON array format and JSONL (one finding per line) and automatically:
- Sorts findings by severity (Critical first)
- Calculates CVSS 4.0 severity scores
- Generates executive summary with risk rating
- Maps OWASP MASTG categories
- Provides formatted proof of concept and remediation sections
See scripts/test-findings.json for the expected JSON structure.
Reference:
references/reporting-templates-ios.mdfor the full executive-summary format, remediation priority matrix, and presentation slide templates. Reference:references/owasp-mastg-ios-checklist.mdfor the full MASTG test catalog (use it to make sure every applicable test has been run before signing off the report). Reference:references/ios-privacy-testing.mdfor the privacy/ATT/Privacy Manifest section of the report.
IPA Modification Workflow
0. Optional: Convert for simulator testing (Apple Silicon)
Before any modification, if you intend to test the app in the iOS simulator instead of a physical device, run simforge convert to patch the Mach-O LC_BUILD_VERSION to PLATFORM_IOSSIMULATOR. This breaks the original code signature, so re-sign (step 3) becomes mandatory.
simforge convert /path/to/Payload/YourApp.app
# Re-signing in step 3 below will also cover this case
Reference:
references/simforge-ios-testing.mdfor the full simulator-based modification + dylib injection flow.
1. Extract
unzip app.ipa -d app-extracted/
cd app-extracted/Payload/YourApp.app/
2. Modify
Binary Patching
Use tools like otool, jtool2, or hex editor to patch binary:
# Example: NOP out jailbreak detection
jtool2 -d YourApp > disassembly.txt
# Or specify architecture: ARCH=arm64 jtool2 -d YourApp > disassembly.txt
# Edit binary with hex editor or jtool2
Resource Modification
Edit Info.plist or embedded resources:
<!-- Example: disable ATS -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
3. Re-sign
# Remove existing signature
codesign --remove-signature YourApp.app
# Embed the provisioning profile INSIDE the .app bundle (codesign has no --provisioning-profile flag;
# the standard workflow is to place the .mobileprovision as embedded.mobileprovision inside the bundle)
cp YourProfile.mobileprovision YourApp.app/embedded.mobileprovision
# Sign wi
*Truncated - read the full file at https://github.com/DragonJAR/iOS-Pentesting-Skill/blob/2096541571c55bc9ffd629a417a950eb591ebbaf/SKILL.md.*