For graph and advanced features, download the full Intel Codex Vault and open it in Obsidian.
Mobile Security (iOS & Android)
Purpose: Comprehensive guide to mobile application security testing, reverse engineering, and exploitation for iOS and Android platforms.
Table of Contents
- Overview
- Android Security Testing
- iOS Security Testing
- Common Mobile Vulnerabilities
- Network Analysis
- Dynamic Instrumentation
- Bypassing Security Controls
- Automated Testing
- Tools Reference
Overview
Mobile Security Testing Scope
Key areas:
- App reverse engineering: Decompile, analyze code, find vulnerabilities
- Runtime analysis: Hook functions, modify behavior, bypass protections
- Network security: HTTPS interception, API security, certificate pinning bypass
- Data storage: Analyze local databases, shared preferences, keychains
- Authentication/authorization: Session management, token security
- Cryptography: Weak algorithms, hardcoded keys, insecure random number generation
- Platform-specific: Android intents, iOS URL schemes, deep linking
Testing Environments
Android:
- Emulator: Android Studio AVD (x86/x64, faster)
- Physical device: Rooted Android phone (Pixel, OnePlus recommended)
- OS version: Test on multiple Android versions (8.0+)
iOS:
- Simulator: Xcode Simulator (limited - no hardware features)
- Physical device: Jailbroken iPhone (iOS 14-16)
- OS version: Test on current and previous iOS versions
Legal & Ethical Considerations
Authorized testing only:
- ✅ Test apps you developed or have permission to test
- ✅ Bug bounty programs (HackerOne, Bugcrowd)
- ✅ Penetration testing engagements with signed contracts
- ❌ Reverse engineering apps without authorization (may violate TOS)
- ❌ Circumventing DRM or software protection for piracy
Responsible disclosure:
- Report vulnerabilities to app developers/vendors
- Allow 90 days for remediation before public disclosure
- Use CVE process for significant vulnerabilities
Android Security Testing
Android Application Structure
APK (Android Package) contents:
app.apk (ZIP file)
│
├── AndroidManifest.xml # App metadata, permissions, components
├── classes.dex # Dalvik bytecode (compiled Java/Kotlin)
├── resources.arsc # Compiled resources
├── res/ # Resources (images, layouts, strings)
│ ├── drawable/
│ ├── layout/
│ └── values/
├── lib/ # Native libraries (.so files)
│ ├── armeabi-v7a/
│ ├── arm64-v8a/
│ ├── x86/
│ └── x86_64/
├── assets/ # Raw asset files
├── META-INF/ # APK signature
│ ├── MANIFEST.MF
│ ├── CERT.RSA
│ └── CERT.SF
└── kotlin/ # Kotlin metadata (if Kotlin app)
Android Reverse Engineering
Step 1: Obtain APK
# Method 1: Download from device (requires adb)
adb shell pm list packages | grep <app_name>
# Output: package:com.example.app
adb shell pm path com.example.app
# Output: package:/data/app/com.example.app-xxx/base.apk
adb pull /data/app/com.example.app-xxx/base.apk app.apk
# Method 2: Download from APK mirror sites
# APKMirror, APKPure, APKMonk (verify integrity!)
# Method 3: Extract from app store (requires tools)
# Use gplaycli or APKUpdater
Step 2: Decompile APK with JADX
# Install JADX:
# https://github.com/skylot/jadx/releases
# Decompile APK to Java source:
jadx app.apk -d output_dir/
# Output structure:
# output_dir/
# ├── sources/ # Decompiled Java code
# │ └── com/example/app/
# │ ├── MainActivity.java
# │ ├── LoginActivity.java
# │ └── utils/
# ├── resources/ # Decoded resources
# └── AndroidManifest.xml
# Or use JADX-GUI:
jadx-gui app.apk
Step 3: Analyze decompiled code
// Example: LoginActivity.java (decompiled)
public class LoginActivity extends AppCompatActivity {
private static final String API_KEY = "sk_live_abc123xyz"; // Hardcoded API key!
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
}
public void loginUser(String username, String password) {
// Vulnerable: No input validation
String query = "SELECT * FROM users WHERE username='" + username + "'"; // SQL injection!
// Insecure: HTTP instead of HTTPS
String url = "http://api.example.com/login"; // Cleartext transmission!
// Weak crypto: MD5 for password hashing
String hashedPassword = MD5(password); // MD5 is broken!
}
}
Step 4: Decode with Apktool (for resources and smali)
# Install Apktool:
# https://ibotpeaches.github.io/Apktool/
# Decode APK:
apktool d app.apk -o app_decoded/
# Output structure:
# app_decoded/
# ├── AndroidManifest.xml # Decoded manifest (readable XML)
# ├── smali/ # Dalvik assembly code
# │ └── com/example/app/
# │ ├── MainActivity.smali
# │ └── LoginActivity.smali
# ├── res/ # Decoded resources
# │ ├── layout/
# │ ├── values/
# │ │ └── strings.xml # App strings
# └── original/ # Original files
# Analyze resources:
cat app_decoded/res/values/strings.xml
Step 5: Analyze AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app">
<!-- Permissions (check for excessive permissions) -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_CONTACTS" /> <!-- Suspicious! -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Exported components (vulnerable if not protected) -->
<activity android:name=".MainActivity"
android:exported="true"> <!-- Exported = accessible by other apps! -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Debuggable flag (should be false in production) -->
<application
android:debuggable="true" <!-- VULNERABLE! Allows debugging -->
android:allowBackup="true" <!-- Data can be backed up -->
android:networkSecurityConfig="@xml/network_security_config">
...
</application>
</manifest>
Key manifest checks:
| Check | Security Impact |
|---|---|
android:debuggable="true" | Allows attaching debugger, inspecting memory |
android:allowBackup="true" | Data can be extracted via adb backup |
android:exported="true" | Component accessible by other apps (intent hijacking) |
| Excessive permissions | Privacy violation, larger attack surface |
android:usesCleartextTraffic="true" | Allows HTTP traffic (MITM risk) |
Android Dynamic Analysis
Step 1: Set up testing device
# Enable Developer Options on Android device:
# Settings → About Phone → Tap "Build number" 7 times
# Enable USB debugging:
# Settings → Developer Options → USB debugging (ON)
# Connect device via USB:
adb devices
# Output: List of devices attached
# ABC123XYZ device
# Root device (if not rooted):
# Use Magisk: https://github.com/topjohnwu/Magisk
# Flash Magisk via custom recovery (TWRP)
Step 2: Install app
# Install APK:
adb install app.apk
# Or install from device:
adb shell pm install /sdcard/app.apk
# Verify installation:
adb shell pm list packages | grep com.example.app
Step 3: Explore app filesystem
# App data directory (requires root):
adb shell
su # Switch to root
cd /data/data/com.example.app/
# Directory structure:
# /data/data/com.example.app/
# ├── cache/ # Cached data
# ├── databases/ # SQLite databases
# │ └── app.db
# ├── shared_prefs/ # SharedPreferences (XML)
# │ └── settings.xml
# ├── files/ # App files
# └── lib/ # Native libraries
# Check for sensitive data:
cat shared_prefs/settings.xml
# Example:
# <string name="api_token">eyJhbGciOiJIUzI1...</string> # Stored token!
cat databases/app.db
# Or use sqlite3:
sqlite3 databases/app.db
sqlite> .tables
sqlite> SELECT * FROM users;
Step 4: Monitor logcat (logs)
# View real-time logs:
adb logcat | grep com.example.app
# Common log leaks:
# - API keys, tokens
# - Passwords (if logged during debugging)
# - Sensitive user data (PII)
# Example log:
# D/LoginActivity: Logging in user: john@example.com with password: password123
Step 5: Intercept network traffic (see Network Analysis section)
Android Static Analysis (Automated)
MobSF (Mobile Security Framework):
# Install MobSF (Docker):
docker pull opensecurity/mobile-security-framework-mobsf
docker run -it -p 8000:8000 opensecurity/mobile-security-framework-mobsf:latest
# Access: http://localhost:8000
# Upload APK → Automated scan:
# - Permissions analysis
# - Manifest security checks
# - Code analysis (hardcoded secrets, SQL injection, etc.)
# - Binary analysis
# - Report generation (PDF)
AndroBugs:
# Install AndroBugs:
git clone https://github.com/AndroBugs/AndroBugs_Framework.git
cd AndroBugs_Framework
# Run analysis:
python androbugs.py -f app.apk
# Output: Detailed report (vulnerabilities, security misconfigurations)
Android Native Library Analysis
Analyzing .so files (ARM/x86 native libraries):
# Extract native library from APK:
unzip app.apk "lib/arm64-v8a/*" -d lib_extracted/
# Analyze with Ghidra:
# File → Import File → lib_extracted/lib/arm64-v8a/libnative.so
# Analyze with ARM:LE:64:v8A processor
# Look for:
# - Hardcoded strings (encryption keys, API endpoints)
# - JNI functions (Java Native Interface bridges)
# - Crypto implementations (custom or weak crypto)
# - Anti-tampering checks
JNI (Java Native Interface) reversing:
// Java side (loads native library):
public class NativeLib {
static {
System.loadLibrary("native"); // Loads libnative.so
}
// Native method declaration:
public native String decrypt(String encrypted);
}
// Native side (C/C++ in libnative.so):
#include <jni.h>
JNIEXPORT jstring JNICALL
Java_com_example_app_NativeLib_decrypt(JNIEnv *env, jobject thiz, jstring encrypted) {
const char *enc_str = (*env)->GetStringUTFChars(env, encrypted, 0);
// Hardcoded AES key (vulnerable!):
char aes_key[] = "0123456789ABCDEF0123456789ABCDEF";
// Decrypt with AES...
char *decrypted = aes_decrypt(enc_str, aes_key);
return (*env)->NewStringUTF(env, decrypted);
}
Modifying & Repackaging APKs
Step 1: Decompile with Apktool
apktool d app.apk -o app_decoded/
Step 2: Modify smali code
# Edit app_decoded/smali/com/example/app/LoginActivity.smali
# Original (license check):
.method public checkLicense()Z
# ... license validation code ...
const/4 v0, 0x0 # Return false (invalid)
return v0
.end method
# Modified (bypass license check):
.method public checkLicense()Z
const/4 v0, 0x1 # Always return true (valid)
return v0
.end method
Step 3: Rebuild APK
apktool b app_decoded/ -o app_modified.apk
Step 4: Sign APK (required for installation)
# Generate keystore (one-time):
keytool -genkey -v -keystore my.keystore -alias mykey -keyalg RSA -keysize 2048 -validity 10000
# Sign APK:
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my.keystore app_modified.apk mykey
# Align APK (optimization):
zipalign -v 4 app_modified.apk app_final.apk
# Install:
adb install app_final.apk
iOS Security Testing
iOS Application Structure
IPA (iOS App Store Package) contents:
app.ipa (ZIP file)
│
└── Payload/
└── App.app/
├── Info.plist # App metadata, bundle ID, permissions
├── App (Mach-O binary) # Main executable (ARM64)
├── embedded.mobileprovision # Provisioning profile
├── _CodeSignature/ # Code signature
├── Frameworks/ # Embedded frameworks
├── Assets.car # Compiled assets
└── Base.lproj/ # Localized resources
iOS Reverse Engineering
Step 1: Obtain IPA
# Method 1: Download from jailbroken device (requires Frida or SSH)
# SSH into device:
ssh root@<device_ip> # Default password: alpine (change it!)
# Locate app:
find /var/containers/Bundle/Application/ -name "*.app"
# Copy IPA:
scp -r root@<device_ip>:/var/containers/Bundle/Application/XXX/App.app ~/Desktop/
# Method 2: Decrypt from App Store (requires jailbroken device)
# Apps downloaded from App Store are encrypted (FairPlay DRM)
# Use frida-ios-dump or Clutch to decrypt:
# frida-ios-dump:
git clone https://github.com/AloneMonkey/frida-ios-dump.git
cd frida-ios-dump
pip3 install -r requirements.txt
# Configure device IP in dump.py
python3 dump.py <bundle_id>
# Example: python3 dump.py com.example.app
# Output: Decrypted IPA saved to current directory
# Method 3: Download from third-party sites (verify integrity!)
Step 2: Analyze with Hopper/Ghidra
# Extract IPA:
unzip app.ipa
# Analyze main binary:
# Payload/App.app/App (Mach-O ARM64 binary)
# Ghidra:
# File → Import File → Payload/App.app/App
# Analyze with AARCH64:LE:64:AppleSilicon processor
# Hopper (macOS/Linux):
hopper -e Payload/App.app/App
# IDA Pro (commercial):
ida64 Payload/App.app/App
Step 3: Class dump (Objective-C apps)
# Install class-dump:
# https://github.com/nygard/class-dump
# Dump Objective-C class headers:
class-dump Payload/App.app/App -H -o headers/
# Output: Objective-C headers (.h files)
# headers/
# ├── AppDelegate.h
# ├── LoginViewController.h
# └── APIManager.h
# Example header:
# @interface LoginViewController : UIViewController
# - (void)loginWithUsername:(NSString *)username password:(NSString *)password;
# - (BOOL)validateLicense:(NSString *)license;
# @end
Step 4: Analyze Info.plist
# View Info.plist:
plutil -p Payload/App.app/Info.plist
# Or convert to XML:
plutil -convert xml1 Payload/App.app/Info.plist -o Info_readable.plist
# Key checks:
# - CFBundleIdentifier: Bundle ID
# - NSAppTransportSecurity: ATS settings (allows HTTP?)
# - UIRequiredDeviceCapabilities: Required capabilities
# - Entitlements: Permissions (location, camera, etc.)
Info.plist security checks:
<!-- Example Info.plist -->
<dict>
<!-- App Transport Security (ATS) disabled (VULNERABLE!) -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/> <!-- Allows HTTP connections! -->
</dict>
<!-- URL schemes (deep linking attack surface) -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string> <!-- myapp:// URL scheme -->
</array>
</dict>
</array>
</dict>
iOS Dynamic Analysis
Step 1: Jailbreak device
# Jailbreak tools (varies by iOS version):
# - checkra1n (iOS 12-14.8.1, semi-tethered)
# - unc0ver (iOS 11-14.8, semi-untethered)
# - Taurine (iOS 14-14.3, semi-untethered)
# - Odyssey (iOS 13-13.7)
# After jailbreak, install essential tools via Cydia:
# - OpenSSH (remote access)
# - Frida (dynamic instrumentation)
# - SSL Kill Switch 2 (bypass SSL pinning)
# - AppSync Unified (install unsigned IPAs)
Step 2: SSH into device
# Install OpenSSH via Cydia
# Connect via SSH (default password: alpine):
ssh root@<device_ip>
# IMPORTANT: Change default password!
passwd # For root
passwd mobile # For mobile user
# Explore filesystem:
cd /var/mobile/Containers/Data/Application/
ls -la
# Each app has a UUID directory
# Find app by bundle ID:
find . -name "*.plist" -exec grep -l "com.example.app" {} \;
Step 3: Analyze app sandbox
# App data directory:
# /var/mobile/Containers/Data/Application/<UUID>/
# Documents/ # User documents
# Library/ # App data
# ├── Caches/ # Cached data
# ├── Preferences/ # User preferences (.plist files)
# └── Cookies/ # Cookies
# tmp/ # Temporary files
# Check for sensitive data:
cd /var/mobile/Containers/Data/Application/<UUID>/Library/Preferences/
# View .plist files:
plutil -p com.example.app.plist
# Example (leaked token):
# "api_token" => "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Step 4: Analyze Keychain (requires tool)
# Install Keychain-Dumper:
# https://github.com/ptoomey3/Keychain-Dumper
# Copy to device:
scp keychain_dumper root@<device_ip>:/tmp/
# Run on device:
ssh root@<device_ip>
cd /tmp
chmod +x keychain_dumper
./keychain_dumper
# Output: All keychain items accessible by current app
# Example:
# Generic Password
# Service: com.example.app
# Account: user@example.com
# Password: supersecret123
Step 5: Monitor system logs
# Install syslog on device (via Cydia)
# View logs:
ssh root@<device_ip>
tail -f /var/log/syslog | grep -i "app"
# Or use Xcode Devices (macOS):
# Xcode → Window → Devices and Simulators → Select device → Open Console
iOS Static Analysis (Automated)
MobSF (supports iOS):
# Upload IPA to MobSF (same as Android):
# http://localhost:8000
# Automated analysis:
# - Binary analysis (strings, symbols)
# - Info.plist checks
# - Code signing verification
# - Entitlements analysis
# - Hardcoded secrets detection
iMAS (iOS Mobile Application Security):
# Source code analysis tool (for developers)
# https://github.com/project-imas/
# Scans Objective-C/Swift source for:
# - Insecure data storage
# - Weak crypto
# - Insecure communication
Common Mobile Vulnerabilities
Insecure Data Storage
Android - SharedPreferences (plaintext):
<!-- /data/data/com.example.app/shared_prefs/settings.xml -->
<map>
<string name="username">john@example.com</string>
<string name="password">password123</string> <!-- PLAINTEXT! -->
<string name="api_token">sk_live_abc123</string>
</map>
Mitigation:
// Encrypt sensitive data with EncryptedSharedPreferences (Android):
import androidx.security.crypto.EncryptedSharedPreferences;
import androidx.security.crypto.MasterKeys;
String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
"secure_prefs",
masterKeyAlias,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
iOS - NSUserDefaults (plaintext):
// Vulnerable (plaintext storage):
[[NSUserDefaults standardUserDefaults] setObject:@"password123" forKey:@"password"];
Mitigation:
// Store sensitive data in Keychain:
#import <Security/Security.h>
// Save to Keychain:
NSDictionary *query = @{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: @"com.example.app",
(__bridge id)kSecAttrAccount: @"user_token",
(__bridge id)kSecValueData: [@"secret_token" dataUsingEncoding:NSUTF8StringEncoding]
};
SecItemAdd((__bridge CFDictionaryRef)query, NULL);
Insecure Communication
Cleartext HTTP traffic:
// Vulnerable (Android):
URL url = new URL("http://api.example.com/login"); // HTTP!
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
Mitigation:
// Use HTTPS:
URL url = new URL("https://api.example.com/login");
// Android Network Security Config (res/xml/network_security_config.xml):
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="false" /> <!-- Block HTTP -->
</network-security-config>
iOS - App Transport Security (ATS) bypass:
<!-- Vulnerable Info.plist (allows HTTP): -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/> <!-- BAD! -->
</dict>
Mitigation:
<!-- Enforce HTTPS only: -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
</dict>
Hardcoded Secrets
API keys in code:
// Android (decompiled):
public class APIClient {
private static final String API_KEY = "sk_live_abc123xyz"; // Exposed!
private static final String SECRET = "mysecret";
}
Finding secrets:
# Android:
jadx app.apk
grep -r "api_key\|secret\|password\|token" output_dir/
# iOS:
strings Payload/App.app/App | grep -i "api\|key\|secret\|token"
Mitigation:
- Store secrets server-side (fetch at runtime)
- Use environment variables (build-time injection)
- Obfuscate strings (minimal protection, not secure)
Weak Cryptography
MD5/SHA1 for passwords:
// Vulnerable:
String hashedPassword = MD5(password); // MD5 is broken!
Weak encryption:
// Vulnerable (hardcoded key, ECB mode):
SecretKeySpec key = new SecretKeySpec("1234567890123456".getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); // ECB mode!
Mitigation:
// Use bcrypt/Argon2 for passwords (server-side):
// For encryption, use AES-GCM with randomly generated keys:
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
SecretKey key = keyGen.generateKey();
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] iv = cipher.getIV(); // Save IV for decryption
byte[] encrypted = cipher.doFinal(plaintext.getBytes());
SQL Injection (Mobile)
Vulnerable SQLite query:
// Android:
String query = "SELECT * FROM users WHERE username='" + username + "'";
Cursor cursor = db.rawQuery(query, null); // SQL injection!
Exploitation:
Username: admin' OR '1'='1' --
Query becomes: SELECT * FROM users WHERE username='admin' OR '1'='1' --'
Result: Bypasses authentication
Mitigation:
// Use parameterized queries:
String query = "SELECT * FROM users WHERE username=?";
Cursor cursor = db.rawQuery(query, new String[]{username});
Intent Hijacking (Android)
Exported component without permission:
<!-- AndroidManifest.xml -->
<activity android:name=".PaymentActivity"
android:exported="true"> <!-- Accessible by other apps! -->
</activity>
Exploitation:
// Malicious app sends intent:
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.example.app", "com.example.app.PaymentActivity"));
intent.putExtra("amount", 0.01); // Manipulate payment amount
intent.putExtra("recipient", "attacker@evil.com");
startActivity(intent);
Mitigation:
<!-- Require permission or set exported=false: -->
<activity android:name=".PaymentActivity"
android:exported="false"> <!-- Only accessible by app -->
</activity>
<!-- Or require custom permission: -->
<permission android:name="com.example.app.PAYMENT_ACCESS"
android:protectionLevel="signature" />
<activity android:name=".PaymentActivity"
android:permission="com.example.app.PAYMENT_ACCESS"
android:exported="true">
</activity>
Network Analysis
HTTPS Interception (MITM Proxy)
Setup Burp Suite for mobile:
Step 1: Configure proxy
# Burp Suite:
# Proxy → Options → Proxy Listeners
# Add: 0.0.0.0:8080 (all interfaces)
# Find computer IP:
ip addr show # Linux
ipconfig # Windows
Step 2: Configure device proxy
Android:
Settings → Wi-Fi → Long-press network → Modify network
→ Advanced → Proxy: Manual
Hostname: <computer_ip>
Port: 8080
iOS:
Settings → Wi-Fi → Tap (i) next to network
→ HTTP Proxy: Manual
Server: <computer_ip>
Port: 8080
Step 3: Install Burp CA certificate
Android:
# Export Burp CA cert:
# Burp → Proxy → Options → Import/Export CA certificate
# Export → Certificate in DER format → Save as burp.der
# Convert to PEM:
openssl x509 -inform DER -in burp.der -out burp.crt
# Get certificate hash (for Android 7+):
openssl x509 -inform PEM -subject_hash_old -in burp.crt | head -1
# Output: 9a5ba575 (example hash)
# Rename cert:
mv burp.crt 9a5ba575.0
# Push to device (requires root):
adb root
adb remount
adb push 9a5ba575.0 /system/etc/security/cacerts/
adb shell chmod 644 /system/etc/security/cacerts/9a5ba575.0
adb reboot
iOS:
# Navigate to Burp CA cert URL on device:
# http://burp/cert
# Download burp.crt
# Install:
# Settings → General → Profile Downloaded → Install
# Settings → General → About → Certificate Trust Settings
# Enable full trust for Burp CA
Step 4: Intercept traffic
# Burp → Proxy → Intercept: ON
# Launch app, observe HTTP/HTTPS requests
# Example intercepted request:
POST /api/login HTTP/1.1
Host: api.example.com
Content-Type: application/json
{"username": "john", "password": "password123"}
# Modify and forward
Bypassing Certificate Pinning
Certificate pinning validates server certificate against known cert/public key.
Detection:
# Android: Search for keywords in decompiled code:
grep -r "CertificatePinner\|TrustManager\|PinningTrustManager" output_dir/
# iOS: Search in binary:
strings Payload/App.app/App | grep -i "pin\|certificate\|ssl"
Bypass methods:
1. Frida script (runtime hooking):
// Android - Bypass OkHttp CertificatePinner:
Java.perform(function() {
var CertificatePinner = Java.use("okhttp3.CertificatePinner");
CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(str, list) {
console.log("[+] Bypassing SSL pinning for: " + str);
return; // Do nothing (skip pinning check)
};
});
// iOS - Bypass NSURLSession pinning:
Interceptor.attach(ObjC.classes.NSURLSession["- URLSession:didReceiveChallenge:completionHandler:"].implementation, {
onEnter: function(args) {
console.log("[+] Bypassing SSL pinning");
var completionHandler = new ObjC.Block(args[4]);
completionHandler.implementation = function(disposition, credential) {
// Accept any certificate
var NSURLSessionAuthChallengeDisposition = {
UseCredential: 0,
PerformDefaultHandling: 1,
CancelAuthenticationChallenge: 2,
RejectProtectionSpace: 3
};
return completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, credential);
};
}
});
2. Xposed/Magisk module (Android):
# Install TrustMeAlready (Magisk module):
# https://github.com/ViRb3/TrustMeAlready
# Or SSLUnpinning (Xposed module):
# Install via Xposed Installer app
# Reboot device
3. SSL Kill Switch (iOS):
# Install via Cydia:
# Search: SSL Kill Switch 2
# Install and reboot
# Toggle in Settings
4. Objection (automated):
# Install Objection:
pip3 install objection
# Patch APK to include Frida gadget:
objection patchapk -s app.apk
# Install patched APK:
adb install app_objection.apk
# Run Objection:
objection explore
# Disable SSL pinning:
android sslpinning disable
Dynamic Instrumentation
Frida Basics
Installation:
# Install Frida on computer:
pip3 install frida frida-tools
# Install Frida server on Android device:
# Download frida-server for your architecture:
# https://github.com/frida/frida/releases
# Example: frida-server-16.0.0-android-arm64
adb push frida-server /data/local/tmp/
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "/data/local/tmp/frida-server &"
# Install Frida on iOS (jailbroken):
# Add Frida repo in Cydia: https://build.frida.re
# Install: Frida
Listing processes:
# List running apps:
frida-ps -U # USB device
# Output:
# PID Name
# ---- --------
# 1234 com.example.app
Attaching to app:
# Spawn app with Frida:
frida -U -f com.example.app -l script.js
# Attach to running app:
frida -U com.example.app -l script.js
Frida Scripting Examples
1. Hooking Java methods (Android):
// Hook LoginActivity.loginUser() method
Java.perform(function() {
var LoginActivity = Java.use("com.example.app.LoginActivity");
LoginActivity.loginUser.implementation = function(username, password) {
console.log("[+] loginUser called!");
console.log(" Username: " + username);
console.log(" Password: " + password);
// Call original method:
var result = this.loginUser(username, password);
console.log(" Result: " + result);
return result;
};
});
2. Bypassing root detection (Android):
Java.perform(function() {
// Hook common root detection methods:
// Method 1: Check for su binary
var File = Java.use("java.io.File");
File.exists.implementation = function() {
var path = this.getAbsolutePath();
if (path.indexOf("su") !== -1) {
console.log("[+] Hiding su binary: " + path);
return false; // Pretend su doesn't exist
}
return this.exists();
};
// Method 2: Check for Magisk/SuperSU
var PackageManager = Java.use("android.app.ApplicationPackageManager");
PackageManager.getPackageInfo.overload('java.lang.String', 'int').implementation = function(packageName, flags) {
if (packageName === "com.topjohnwu.magisk" || packageName === "eu.chainfire.supersu") {
console.log("[+] Hiding root package: " + packageName);
throw Java.use("android.content.pm.PackageManager$NameNotFoundException").$new();
}
return this.getPackageInfo(packageName, flags);
};
});
3. Hooking Objective-C methods (iOS):
// Hook UIViewController viewDidLoad
if (ObjC.available) {
var UIViewController = ObjC.classes.UIViewController;
Interceptor.attach(UIViewController['- viewDidLoad'].implementation, {
onEnter: function(args) {
var controller = new ObjC.Object(args[0]);
console.log("[+] viewDidLoad called on: " + controller.$className);
}
});
// Hook specific class method:
var LoginVC = ObjC.classes.LoginViewController;
Interceptor.attach(LoginVC['- loginWithUsername:password:'].implementation, {
onEnter: function(args) {
var username = new ObjC.Object(args[2]).toString();
var password = new ObjC.Object(args[3]).toString();
console.log("[+] Login attempt:");
console.log(" Username: " + username);
console.log(" Password: " + password);
}
});
}
4. Dumping memory:
// Dump all loaded modules:
Process.enumerateModules({
onMatch: function(module) {
console.log("Module: " + module.name + " @ " + module.base);
},
onComplete: function() {}
});
// Dump memory region:
var baseAddr = Module.findBaseAddress("libnative.so");
var size = 0x1000; // 4KB
console.log(hexdump(baseAddr, { length: size }));
5. Tracing all function calls:
// Android - Trace all methods in a class:
Java.perform(function() {
var LoginActivity = Java.use("com.example.app.LoginActivity");
LoginActivity.$init.implementation = function() {
console.log("[+] LoginActivity constructor");
return this.$init();
};
var methods = LoginActivity.class.getDeclaredMethods();
methods.forEach(function(method) {
var methodName = method.getName();
if (LoginActivity[methodName]) {
LoginActivity[methodName].implementation = function() {
console.log("[+] Called: " + methodName);
return this[methodName].apply(this, arguments);
};
}
});
});
Objection (Frida REPL)
Objection provides interactive Frida REPL with common commands.
# Launch Objection:
objection -g com.example.app explore
# Common commands:
android hooking list classes # List all classes
android hooking search classes LoginActivity # Search classes
android hooking list class_methods com.example.app.LoginActivity # List methods
android hooking watch class com.example.app.LoginActivity # Hook all methods
android sslpinning disable # Bypass SSL pinning
android root disable # Bypass root detection
ios hooking list classes # List Objective-C classes
ios hooking search classes LoginViewController
ios hooking watch class LoginViewController
memory dump all <output_file> # Dump memory
Bypassing Security Controls
Root/Jailbreak Detection Bypass
Android root detection methods:
1. Check for su binary:
File suFile = new File("/system/xbin/su");
if (suFile.exists()) {
// Rooted!
}
Bypass with Frida (see Frida examples above)
2. Check for root apps (Magisk, SuperSU):
PackageManager pm = getPackageManager();
try {
pm.getPackageInfo("com.topjohnwu.magisk", 0);
// Magisk installed = rooted
} catch (PackageManager.NameNotFoundException e) {
// Not rooted
}
3. Check build tags:
String buildTags = Build.TAGS;
if (buildTags.contains("test-keys")) {
// Custom ROM = likely rooted
}
iOS jailbreak detection:
1. Check for Cydia/jailbreak apps:
if ([[NSFileManager defaultManager] fileExistsAtPath:@"/Applications/Cydia.app"]) {
// Jailbroken
}
2. Check for write access to /private:
if ([[NSFileManager defaultManager] isWritableFileAtPath:@"/private"]) {
// Jailbroken
}
3. Fork() restriction:
if (fork() >= 0) {
// Jailbroken (sandboxed apps can't fork)
}
Bypass with Frida:
// iOS jailbreak detection bypass:
var fopen = new NativeFunction(Module.findExportByName(null, 'fopen'), 'pointer', ['pointer', 'pointer']);
Interceptor.replace(fopen, new NativeCallback(function(path, mode) {
var pathStr = Memory.readUtf8String(path);
if (pathStr.indexOf("Cydia") !== -1 || pathStr.indexOf("/bin/bash") !== -1) {
console.log("[+] Hiding jailbreak file: " + pathStr);
return ptr(0); // Pretend file doesn't exist
}
return fopen(path, mode);
}, 'pointer', ['pointer', 'pointer']));
Debugger Detection Bypass
Android debugger detection:
// Check if debugger attached:
if (Debug.isDebuggerConnected()) {
// Debugger detected
}
// Check debuggable flag:
if ((getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
// App is debuggable
}
iOS debugger detection:
// Check for debugger via sysctl:
#include <sys/sysctl.h>
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};
struct kinfo_proc info;
size_t size = sizeof(info);
sysctl(mib, 4, &info, &size, NULL, 0);
if (info.kp_proc.p_flag & P_TRACED) {
// Debugger attached
}
Bypass:
- Patch binary to remove checks (static analysis)
- Hook detection functions with Frida (dynamic)
Emulator/Simulator Detection Bypass
Android emulator detection:
// Check for emulator properties:
String brand = Build.BRAND; // "generic" on emulators
String device = Build.DEVICE; // "generic" on emulators
String model = Build.MODEL; // "sdk" on emulators
// Check for Genymotion:
if (Build.PRODUCT.contains("vbox")) {
// Genymotion emulator
}
Bypass:
# Modify build.prop on emulator:
adb root
adb remount
adb shell
vi /system/build.prop
# Change:
# ro.build.product=generic -> ro.build.product=OnePlus7
# ro.product.model=sdk -> ro.product.model=OnePlus 7
reboot
Automated Testing
Mobile App Scanners
QARK (Quick Android Review Kit):
# Install QARK:
pip3 install qark
# Analyze APK:
qark --apk app.apk
# Output: Vulnerability report (manifest issues, code vulnerabilities, etc.)
MobSF (Mobile Security Framework):
# See Android/iOS Static Analysis sections for setup
# Features:
# - Automated vulnerability scanning
# - Code analysis (SAST)
# - Binary analysis
# - API testing (dynamic)
# - Report generation
Automated Fuzzing
Drozer (Android Intent fuzzing):
# Install Drozer:
# https://github.com/FSecureLABS/drozer
# Install agent APK on device:
adb install drozer-agent.apk
# Start drozer server on device (via agent app)
# Connect from computer:
drozer console connect
# List attack surface:
dz> run app.package.attacksurface com.example.app
# Output:
# Exported Activities: 3
# Exported Services: 1
# Exported Broadcast Receivers: 2
# Fuzz exported components:
dz> run app.activity.start --component com.example.app com.example.app.PaymentActivity --extra string amount "0.01"
Tools Reference
Android Tools
| Tool | Purpose | Platform |
|---|---|---|
| JADX | APK decompiler (DEX to Java) | Win/Linux/macOS |
| Apktool | APK decoder/rebuilder (smali) | Win/Linux/macOS |
| MobSF | Automated security testing | Web-based |
| Frida | Dynamic instrumentation | Win/Linux/macOS |
| Objection | Frida REPL for mobile | Win/Linux/macOS |
| Drozer | Android security assessment | Win/Linux/macOS |
| AndroBugs | Static analyzer | Linux |
| QARK | Quick security scanner | Linux |
| adb | Android Debug Bridge | Win/Linux/macOS |
iOS Tools
| Tool | Purpose | Platform |
|---|---|---|
| frida-ios-dump | Decrypt App Store IPAs | Linux/macOS |
| Clutch | Decrypt IPAs (alternative) | iOS (jailbroken) |
| class-dump | Extract Objective-C headers | macOS/Linux |
| Hopper | Disassembler/decompiler | macOS/Linux |
| MobSF | Automated security testing | Web-based |
| Frida | Dynamic instrumentation | Win/Linux/macOS |
| Objection | Frida REPL for mobile | Win/Linux/macOS |
| SSL Kill Switch | Bypass SSL pinning | iOS (Cydia) |
| Keychain-Dumper | Extract Keychain items | iOS (jailbroken) |
| iMAS | Source code scanner | macOS |
Multi-Platform Tools
| Tool | Purpose | Platform |
|---|---|---|
| Burp Suite | HTTPS interception/fuzzing | Win/Linux/macOS |
| Ghidra | Binary analysis (ARM/x86) | Win/Linux/macOS |
| IDA Pro | Disassembler (commercial) | Win/Linux/macOS |
| Frida | Runtime hooking | Win/Linux/macOS |
| Radare2 | RE framework | Win/Linux/macOS |
| Wireshark | Network traffic analysis | Win/Linux/macOS |
Learning Resources
Documentation
- OWASP Mobile Security Testing Guide (MSTG): https://owasp.org/www-project-mobile-security-testing-guide/
- OWASP Mobile Top 10: https://owasp.org/www-project-mobile-top-10/
- Android Security Documentation: https://source.android.com/security
- iOS Security Guide: https://www.apple.com/business/docs/iOS_Security_Guide.pdf
Books
- Mobile Application Penetration Testing by Vijay Kumar
- Android Hacker's Handbook by Joshua J. Drake et al.
- iOS Hacker's Handbook by Charlie Miller et al.
- The Mobile Application Hacker's Handbook by Dominic Chell et al.
Online Courses
- Mobile Application Security and Penetration Testing (eLearnSecurity eMAPT)
- SANS SEC575: Mobile Device Security and Ethical Hacking
- Pentester Academy: Mobile Security
Practice Apps
- DVIA (Damn Vulnerable iOS App): https://github.com/prateek147/DVIA-v2
- InsecureBankv2 (Android): https://github.com/dineshshetty/Android-InsecureBankv2
- AndroGoat (Android): https://github.com/satishpatnayak/AndroGoat
- OWASP iGoat (iOS): https://github.com/OWASP/igoat
Related SOPs
Analysis:
- Reverse Engineering - Mobile binary analysis (APK/IPA reverse engineering)
- Cryptography Analysis - SSL pinning, certificate analysis, crypto failures
- Malware Analysis - Malicious mobile app analysis
Pentesting & Security:
- Web Application Security - Mobile API and backend testing
- Firmware Reverse Engineering - IoT and embedded mobile device analysis
- Vulnerability Research - Finding mobile platform vulnerabilities
- Bug Bounty Hunting - Responsible mobile app vulnerability disclosure
- Detection Evasion Testing - Bypassing mobile security controls
- Forensics Investigation - Mobile device forensics