Skip to main content

iOS Security Fundamentals

Sandbox & Privilege Separation

iOS apps run under the mobile user identity while core system processes run as root. Each app operates in its own sandbox (private/var/mobile/Applications/{random ID}) with strict restrictions enforced by the Trusted BSD (MAC) Mandatory Access Control Framework.

Data Protection Classes

iOS provides four protection classes using AES encryption tied to the device UID and user passcode:
ClassAccessibility
NSFileProtectionCompleteOnly when device is unlocked
NSFileProtectionCompleteUnlessOpenAfter first unlock, even if locked again
NSFileProtectionCompleteUntilFirstUserAuthenticationAfter first post-boot unlock (default from iOS 7)
NSFileProtectionNoneProtected only by device UID

The Keychain

The Keychain is an encrypted container for sensitive data (tokens, passwords, certificates). Encrypted with AES using a key derived from PBKDF2(user passcode, device UID). Access is controlled by securityd based on app entitlements. Important: Keychain data persists after app uninstallation. Always clear Keychain items on first launch.
// Clear Keychain on first launch
let userDefaults = UserDefaults.standard
if userDefaults.bool(forKey: "hasRunBefore") == false {
    // Remove Keychain items here
    userDefaults.set(true, forKey: "hasRunBefore")
    userDefaults.synchronize()
}
Keychain access levels (kSecAttrAccessible):
  • kSecAttrAccessibleWhenUnlocked — only when device is unlocked
  • kSecAttrAccessibleAfterFirstUnlock — after first post-reboot unlock
  • kSecAttrAccessibleAlways — always (not recommended for sensitive data)
  • kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly — requires device passcode, not in backups

IPA Structure

An IPA is a ZIP file. Rename to .zip and unzip to explore:
<AppName>.app/
  Info.plist            — configuration (permissions, URL schemes, ATS settings)
  _CodeSignature/       — digital signatures for all bundle files
  Assets.car            — compressed graphical assets
  Frameworks/           — native libraries (.dylib, .framework)
  PlugIns/              — app extensions (.appex)
  <AppBinary>           — main executable

Info.plist Key Areas

# Convert binary plist to XML
plutil -convert xml1 Info.plist          # macOS
apt install libplist-utils
plistutil -i Info.plist -o Info_xml.plist # Linux

# Find sensitive entries
grep -i CFBundleURLTypes Info.plist      # Custom URL schemes
grep -i NSAppTransportSecurity Info.plist # ATS config
grep -i UsageDescription Info.plist      # Permission strings

Data Paths

# Bundle path (read-only, not backed up)
/var/containers/Bundle/Application/<UUID>/<AppName>.app

# Data paths (backed up by default)
/var/mobile/Containers/Data/Application/<UUID>/Documents/
/var/mobile/Containers/Data/Application/<UUID>/Library/
/var/mobile/Containers/Data/Application/<UUID>/Library/Preferences/
/var/mobile/Containers/Data/Application/<UUID>/tmp/

# Use objection to get paths easily
env

Static Analysis

Binary Security Properties

# PIE (randomized load address)
otool -hv <app-binary> | grep PIE

# Stack canaries
otool -I -v <app-binary> | grep stack_chk

# ARC (Automatic Reference Counting)
otool -I -v <app-binary> | grep objc_release

# Encryption check
otool -arch all -Vl <app-binary> | grep -A5 LC_ENCRYPT

Disassembly & Decompilation

# Basic binary inspection
otool -Vh DVIA-v2                    # Compilation attributes
otool -L DVIA-v2                     # Third-party libraries
otool -tV DVIA-v2                    # Disassemble text section
otool -oV DVIA-v2                    # Objective-C segment

# Extract class declarations
class-dump <app-binary>

# Best options for full decompilation:
# Hopper: https://www.hopperapp.com
# IDA Pro / IDA Free: https://hex-rays.com
# Malimite: https://github.com/LaurieWired/Malimite
# Ghidra: https://ghidra-sre.org

Insecure Function Detection

# Weak hashing
otool -Iv <app> | grep -w "_CC_MD5"
otool -Iv <app> | grep -w "_CC_SHA1"

# Insecure random functions
otool -Iv <app> | grep -w "_random"

# Dangerous memory functions
otool -Iv <app> | grep -w "_gets"
otool -Iv <app> | grep -w "_sprintf"

Dynamic Analysis

Setup & Listing Apps

# List installed apps and bundle IDs
frida-ps -Uai

# Find app data directory
find /private/var/containers -name "<AppName>*"

# Process info
ps -ef | grep -i <app-name>
lsof -p <pid> | grep -i "/containers" | head -n 1

Jailbreak & Anti-Debug Detection Bypass

Apps check for:
  • Presence of /Applications/Cydia.app, /Library/MobileSubstrate/MobileSubstrate.dylib
  • Ability to call fork() or system()
  • Known jailbreak processes (Cydia, Substrate)
  • URL schemes like cydia://
  • DYLD_INSERT_LIBRARIES environment variable

Data Storage Testing

# Dump NSUserDefaults
ios nsuserdefaults get

# Read plist via objection
ios plist cat /private/var/mobile/Containers/Data/Application/<UUID>/Library/Preferences/com.app.bundle.plist

# Dump cookies
ios cookies get --json

# Dump keychain
ios keychain dump

# Dump credentials from NSURLCredentialStorage
ios nsurlcredentialstorage dump

Snapshot Protection

When an app is backgrounded, iOS saves a screen snapshot. Prevent sensitive data leakage:
func applicationDidEnterBackground(_ application: UIApplication) {
    let myBanner = UIImageView(image: #imageLiteral(resourceName: "overlayImage"))
    myBanner.frame = UIScreen.main.bounds
    window?.addSubview(myBanner)
}

Local Authentication Bypass

Objection Biometrics Bypass

ios ui biometrics_bypass
This hooks evaluatePolicy via Frida to always return True.

Frida Hook (DVIA-v2 Example)

// Hook evaluatePolicy to always succeed
var hook = ObjC.classes["LAContext"]["- evaluatePolicy:localizedReason:reply:"];
Interceptor.attach(hook.implementation, {
  onEnter: function (args) {
    // Modify reply block to always call with YES
    var block = new ObjC.Block(args[4]);
    var originalImpl = block.implementation;
    block.implementation = function (error, success) {
      return originalImpl(null, true);
    };
  }
});

Network Communication

Certificate Pinning Bypass

# Using Objection
ios sslpinning disable

# Find pinning indicators in binary
rabin2 -zz <binary> | grep -i "pinning\|certificate\|sha256"

Proxy Setup

  1. Install Burp CA certificate on the device (Settings > General > VPN & Device Management)
  2. Trust the certificate (Settings > General > About > Certificate Trust Settings)
  3. Configure the device to use Burp as HTTP proxy
  4. For certificate pinning, use Objection or a Frida script to bypass

Memory Analysis

# Dump memory with Objection
memory dump all mem.dump

# Analyze strings
strings mem.dump > strings.txt
rabin2 -ZZ mem.dump > strings.txt

# Real-time memory search with r2frida
r2 frida://usb//<app_name>
[0x00000000]> /\ <search_term>

Automated Analysis with MobSF

docker pull opensecurity/mobile-security-framework-mobsf
docker run -it -p 8000:8000 opensecurity/mobile-security-framework-mobsf:latest
Upload your IPA for automatic static analysis covering binary properties, Info.plist, URL schemes, ATS settings, and more.

References

Build docs developers (and LLMs) love