Mobile apps routinely handle your banking credentials, health data, private messages, and location history — and they’re tested far less rigorously than web applications. In most mobile pentesting engagements, we find at least one critical vulnerability within the first hour. Usually more.

TL;DR

  • Mobile security testing covers two major platforms: Android (APK analysis, ADB, root) and iOS (IPA analysis, jailbreak, Objection)
  • The OWASP Mobile Application Security Verification Standard (MASVS) defines what a secure mobile app looks like — use it as your checklist
  • Most critical findings come from: hardcoded secrets, insecure data storage, broken authentication, missing certificate pinning, and insecure API communication
  • Frida + Objection is the power combo for dynamic instrumentation on both platforms
  • You don’t need a physical device for most testing — emulators and simulators cover the majority of attack surface

The Mobile Attack Surface

Mobile applications are more complex than they look. A single app might include:

  • The APK/IPA itself — the package distributed to devices
  • Native code (C/C++ via JNI on Android, Swift/Objective-C on iOS)
  • A backend API (usually REST or GraphQL)
  • WebView components (essentially an embedded browser)
  • Local data storage (SQLite, SharedPreferences, Keychain, files)
  • Inter-process communication (IPC, Intents, deep links)
  • Third-party SDKs (analytics, ads, crash reporting — each a potential risk)

A thorough mobile pentest touches all of these. This guide walks through each layer.


The Framework: OWASP MASVS

The Mobile Application Security Verification Standard (MASVS) is the definitive checklist for mobile app security. It defines controls across categories:

CategoryFocus
MASVS-STORAGESensitive data not stored insecurely on device
MASVS-CRYPTOProper cryptography usage
MASVS-AUTHAuthentication and session management
MASVS-NETWORKSecure network communication
MASVS-PLATFORMPlatform security controls respected
MASVS-CODESecure coding practices
MASVS-RESILIENCEResistance to reverse engineering and tampering

The companion guide MASTG (Mobile Application Security Testing Guide) provides specific test cases for each control. Both are free and maintained at mas.owasp.org.


Lab Setup

Android Testing Lab

Option 1: Physical device (best)

  • Enable Developer Options → USB Debugging
  • Root the device (Magisk is the current standard)
  • Install Burp Suite CA certificate in system trust store

Option 2: Android Emulator (recommended for most testing)

Terminal window
# Install Android Studio + Android SDK
# Create AVD (Android Virtual Device) with API level 29-33
# Use a non-Google Play image (they're rootable)
# Launch emulator with writable system partition
emulator -avd Pixel_6_API_31 -writable-system

Core Android tools:

Terminal window
# ADB — Android Debug Bridge (essential)
adb devices
adb shell
adb install target.apk
# jadx — APK decompiler (GUI + CLI)
jadx-gui target.apk
# apktool — decode/rebuild APK resources
apktool d target.apk -o target_decoded/
# Frida — dynamic instrumentation
pip install frida-tools
frida-server # runs on device
# Objection — Frida wrapper for mobile pentesting
pip install objection
objection -g com.target.app explore

iOS Testing Lab

Option 1: Jailbroken device

  • Checkra1n or Dopamine for modern iPhones
  • Install SSH, Frida server via Cydia/Sileo
  • Install Burp Suite CA via Settings → Profile

Option 2: iOS Simulator (limited)

  • Runs on macOS only, doesn’t support all hardware features
  • Useful for static analysis and some traffic testing
  • No real certificate pinning testing without a real device

Core iOS tools:

Terminal window
# ipa-tools / ipatool — extract IPA from device or App Store
ipatool download --bundle-id com.target.app
# class-dump / dsdump — dump Objective-C class headers
class-dump -H TargetApp.app -o headers/
# Hopper / Ghidra — reverse engineer Swift/ObjC binaries
# frida-ios-dump — dump decrypted IPA from jailbroken device
frida-ios-dump -u root -H 192.168.1.100 "Target App"
# Objection — same tool works on iOS
objection --gadget "Target App" explore

Phase 1: Static Analysis

Static analysis means examining the app without running it. You’re looking for secrets, misconfigurations, and code-level vulnerabilities.

Decompile the APK (Android)

Terminal window
# Extract and decompile
apktool d target.apk -o target_decoded/
# Convert classes.dex to jar, then decompile to Java source
d2j-dex2jar target.apk -o target.jar
jadx-gui target.jar
# Or directly with jadx
jadx -d target_source/ target.apk

jadx produces readable Java source code from the compiled APK. It’s rarely perfect, but it’s close enough for security review.


Hunt for Hardcoded Secrets

The most common critical finding in mobile apps. Developers hardcode API keys, tokens, credentials, and private keys directly in the source.

Terminal window
# Grep for common patterns
grep -r "api_key\|api_secret\|password\|private_key\|secret" target_source/
grep -r "AWS\|firebase\|stripe\|twilio" target_source/
grep -r "http://" target_source/ # cleartext URLs
# Search in decompiled resources
grep -r "api" target_decoded/res/values/strings.xml

Real findings from mobile engagements:

  • Firebase API keys with unrestricted write access
  • AWS credentials hardcoded in a config file bundled in the APK
  • Admin API tokens committed directly in Kotlin source code
  • Private RSA keys embedded as string constants

Check the Manifest (Android)

The AndroidManifest.xml defines the app’s permissions, exported components, and security flags:

Terminal window
cat target_decoded/AndroidManifest.xml

Look for:

<!-- Backup enabled — allows adb backup to extract private data -->
android:allowBackup="true"
<!-- Exported components — accessible from other apps -->
<activity android:exported="true" ...>
<receiver android:exported="true" ...>
<provider android:exported="true" ...>
<!-- Network cleartext allowed -->
<application android:usesCleartextTraffic="true">
<!-- Debuggable in production -->
android:debuggable="true"

Each of these is a finding. Exported components are particularly interesting — they can be invoked from other apps, potentially bypassing authentication.


Check Info.plist and Entitlements (iOS)

Terminal window
# Extract from IPA
unzip target.ipa
cat Payload/Target.app/Info.plist
# Check entitlements
codesign -d --entitlements :- Payload/Target.app/Target

Look for:

  • NSAllowsArbitraryLoads: true in NSAppTransportSecurity — disables TLS validation
  • Overly permissive entitlements (keychain sharing groups, etc.)
  • URL schemes that could be hijacked

Analyze Native Libraries

Many apps include native .so (Android) or .dylib (iOS) libraries. Static analysis tools:

Terminal window
# Check what functions are exported from a .so
nm -D libnative.so
# Disassemble in Ghidra or Ghidra with MCP
# Look for: hardcoded strings, crypto operations, anti-debug checks

Phase 2: Dynamic Analysis

Dynamic analysis means running the app and observing its behavior — traffic, memory, filesystem, and runtime code.

Traffic Interception

The most productive setup: route the device’s traffic through Burp Suite.

Android setup:

Terminal window
# Install Burp CA cert on device
# Export cert from Burp → DER format
# For API 29+, you need system-level trust (requires root)
adb push burp.der /sdcard/
adb shell
su
cp /sdcard/burp.der /system/etc/security/cacerts/
chmod 644 /system/etc/security/cacerts/burp.der
# Set device proxy to your machine IP:8080

Objection one-liner for emulator:

Terminal window
objection -g com.target.app explore --startup-command "android sslpinning disable"

Once traffic flows through Burp, exercise all app features. Capture:

  • Authentication flows (login, registration, password reset)
  • Data fetch requests (API calls returning user data)
  • State-changing requests (profile updates, payments, settings)
  • Background requests (telemetry, analytics, push registration)

SSL Pinning Bypass

Modern apps implement certificate pinning — they check that the server’s certificate matches a hardcoded value, rejecting Burp’s intercepting certificate. This needs to be bypassed for traffic analysis.

Method 1: Objection (easiest)

Terminal window
objection -g com.target.app explore
# After attaching:
android sslpinning disable

Objection hooks common pinning methods (OkHttp, TrustManager, Conscrypt) and disables them at runtime.

Method 2: Frida script (more control)

// Disable Android TrustManager validation
Java.perform(function() {
var TrustManager = Java.registerClass({
name: 'com.test.TrustManager',
implements: [Java.use('javax.net.ssl.X509TrustManager')],
methods: {
checkClientTrusted: function(chain, authType) {},
checkServerTrusted: function(chain, authType) {},
getAcceptedIssuers: function() { return []; }
}
});
// Install custom TrustManager
});

Method 3: Patch the APK

If Frida/Objection doesn’t work (e.g., the app detects instrumentation):

Terminal window
# Decompile
apktool d target.apk -o target_decoded/
# Find pinning code in smali and remove/patch it
# Find the OkHttpClient builder or custom SSLContext calls
# Rebuild and sign
apktool b target_decoded/ -o target_patched.apk
keytool -genkey -v -keystore test.jks -alias test -keyalg RSA
jarsigner -keystore test.jks target_patched.apk test
zipalign 4 target_patched.apk target_final.apk
adb install target_final.apk

Filesystem Analysis

What does the app write to disk? Local storage often contains sensitive data that shouldn’t persist.

Terminal window
# Pull the app's data directory (requires root or ADB backup)
adb shell
run-as com.target.app # on debuggable apps
ls /data/data/com.target.app/
├── databases/ # SQLite databases
├── shared_prefs/ # XML key-value store
├── files/ # Arbitrary files
├── cache/ # Should not contain sensitive data
└── no_backup/ # Excluded from ADB backup

Look for:

Terminal window
# SQLite databases — open with sqlite3
sqlite3 /data/data/com.target.app/databases/app.db
.tables
SELECT * FROM sessions;
SELECT * FROM users;
# SharedPreferences — plaintext XML
cat /data/data/com.target.app/shared_prefs/prefs.xml
# Common bad findings: auth tokens, user IDs, flags like "is_premium=true"
# Logcat — app logging to system log
adb logcat | grep com.target.app
# Watch for tokens, passwords, API responses logged in cleartext

iOS equivalent:

Terminal window
# On jailbroken device or via Objection
objection -g "Target App" explore
ios pasteboard monitor # watch clipboard
ios nsuserdefaults get # read NSUserDefaults
ios keychain dump # dump Keychain items
ls /var/mobile/Containers/Data/Application/<UUID>/Documents/

Runtime Instrumentation with Frida

Frida lets you hook any function in the app at runtime, read arguments, modify return values, and call arbitrary functions.

// Hook a specific function — read its arguments
Java.perform(function() {
var TargetClass = Java.use("com.target.app.AuthManager");
TargetClass.validateToken.implementation = function(token) {
console.log("[*] validateToken called with: " + token);
var result = this.validateToken(token); // call original
console.log("[*] Result: " + result);
return result; // or return true; to always validate
};
});
Terminal window
# Run a Frida script against a running app
frida -U -n com.target.app -l hook_auth.js
# List classes matching a pattern
frida -U -n com.target.app -e "Java.perform(function(){ Java.enumerateLoadedClasses({onMatch: function(n,h){ if(n.indexOf('Auth') >= 0) console.log(n); }, onComplete: function(){}}) })"

Frida is how you defeat custom certificate pinning, bypass root detection, patch in-memory checks, and extract encryption keys.


Bypass Root/Jailbreak Detection

Many banking and enterprise apps detect rooted/jailbroken devices and refuse to run. Standard bypass:

Terminal window
# Objection — one command
android root disable
ios jailbreak disable
# Frida script approach — hook the specific detection method
# First, find the class/method via static analysis

Common detection methods to hook:

  • RootBeer.isRooted() on Android
  • SHAMobileKit.isJailbroken() on iOS
  • Custom checks: File.exists("/su"), File.exists("/system/xbin/su")
  • Checking for Magisk Manager, Cydia, etc.

Phase 3: API and Backend Testing

The mobile app is just a client. The real attack surface is the backend API it talks to.

Once you have traffic flowing through Burp, test the API exactly like a web app:

  • IDOR/BOLA: Swap user IDs in API requests
  • Broken authentication: Test token expiration, logout doesn’t invalidate tokens
  • Missing authorization: Call admin endpoints from a regular user account
  • Input validation: Inject SQL, XSS, command injection in any string field
  • Rate limiting: Rapid repeated requests to login, OTP, password reset endpoints

The most common mobile-specific API finding: the mobile app API has no rate limiting because it was assumed only the official app would call it. Add Burp Intruder and enumerate away.


Phase 4: Platform-Specific Vulnerabilities

Android Intents are messages between app components. Exported components accept Intents from any app on the device:

Terminal window
# List exported activities, services, receivers
adb shell dumpsys package com.target.app | grep -A3 "Activity\|Service\|Receiver"
# Launch an exported activity directly — bypasses authentication
adb shell am start -n com.target.app/.AdminActivity
adb shell am start -n com.target.app/.PasswordResetActivity --es "user_id" "admin"
# Send a broadcast to an exported receiver
adb shell am broadcast -a com.target.app.ACTION_RESET -n com.target.app/.ResetReceiver

Deep links (myapp://action) should be validated — attackers can craft URLs that trigger sensitive actions:

myapp://transfer?to=attacker&amount=10000

If the app processes this without confirmation, it’s a client-side CSRF equivalent.


Android: WebView Vulnerabilities

Apps with embedded WebViews that load attacker-controlled URLs can expose dangerous capabilities:

// Vulnerable: JavaScript enabled + Java bridge exposed
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new JavaBridge(), "Android");
// If attacker controls loaded URL:
// <script>Android.sensitiveMethod()</script>
// → executes native Java code from a web page

Look for addJavascriptInterface calls in decompiled code — they’re often the path to remote code execution.


iOS: URL Scheme Hijacking

iOS URL schemes (myapp://) can be registered by any app. Malicious apps can register the same scheme and intercept OAuth callbacks or deep links:

myapp://oauth?code=AUTHORIZATION_CODE

If a banking app sends you to a browser for OAuth and expects a redirect back via bankapp://callback, a malicious app registered for the same scheme can steal the OAuth code.


iOS: Keychain Security

The iOS Keychain stores secrets securely — but apps sometimes use it incorrectly:

Terminal window
# Dump keychain items via Objection on jailbroken device
ios keychain dump
# Look for:
# - kSecAttrAccessibleAlways (survives device lock)
# - kSecAttrAccessibleAlwaysThisDeviceOnly
# These are wrong — secrets should only be accessible when device is unlocked

Top 10 Mobile Findings in Real Engagements

From mobile penetration testing engagements across industries, these are the most common critical and high findings:

FindingPlatformTypical Impact
Hardcoded API keys/tokensBothFull backend access
Insecure data storage (plaintext tokens in SharedPrefs/NSUserDefaults)BothToken theft on device access
Missing SSL pinningBothTraffic interception, MITM
Broken backend authorization (IDOR/BOLA)BothData theft across accounts
Exported Android componentsAndroidAuthentication bypass
Sensitive data in logsBothData leakage
Weak session management (tokens never expire)BothPersistent account access
WebView JavaScript bridge exposureAndroidRemote code execution
Insecure direct object reference in file downloadBothUnauthorized file access
OAuth code interception via URL schemeiOSAccount takeover

Building a Mobile Pentest Report

Structure findings by severity, include:

  • Vulnerability name and MASVS control ID (e.g., MASVS-STORAGE-1)
  • Description of what was found
  • Steps to reproduce — precise, reproducible
  • Impact — what an attacker can do
  • Evidence — screenshots, traffic captures, Frida output
  • Remediation — specific code fix or configuration change

Reference the OWASP MASTG for remediation guidance on every finding.


What You Can Do Today

If you’re a developer:

  1. Run grep -r "password\|api_key\|secret" src/ on your mobile codebase — remove any hardcoded credentials
  2. Check your Android Manifest for android:exported="true" — any exported component that doesn’t need to be external should be set to false
  3. Use the platform keychain (Android Keystore, iOS Keychain) for any credentials — never SharedPreferences or NSUserDefaults
  4. Implement SSL pinning with a pinning library (TrustKit, OkHttp CertificatePinner) — and make it updatable without an app release

If you’re a security tester new to mobile:

  1. Install jadx, apktool, Frida, and Objection — that’s your full Android toolkit
  2. Start with static analysis — jadx-gui an APK and grep for api_key, password, http:// — you’ll find something within minutes
  3. Practice on intentionally vulnerable apps: DIVA (Android), OWASP iGoat (iOS)
  4. Read the OWASP MASTG — it has step-by-step test cases for every vulnerability

If you’re a CISO or security manager:

  1. Mobile apps should receive the same level of security testing as web apps — if they’re not in scope, add them
  2. Require MASVS Level 1 compliance for all mobile apps before release
  3. Use automated SAST tools on mobile codebases (MobSF can scan APKs automatically in CI)


Sources