Mobile App Pentest Checklist

The OWASP MASVS/MASTG checklist turned into a how-to-test field guide for Android and iOS: static prep, insecure storage, cryptography, auth/session, network and cert pinning, platform interaction (exported components, deep links, WebView), anti-reversing, privacy, and runtime hooking — each with the scenario, the real command (jadx, apktool, frida, objection, adb, MobSF), the steps, the finding, and the fix.

LazyHackers.in — Checklist

📱 Mobile App Pentest Checklist

Android + iOS (MASVS), item by item: scenario · command · steps · the finding · the fix

☰   How to use this guide

Mobile testing is four streams: static (decompile, read the code/manifest), dynamic (Frida/objection hooking on a rooted/jailbroken device), traffic (proxy + bypass cert pinning), and backend (the API the app talks to). The app is just a client — real damage is usually API-side, so pair this with the API checklist. Tags below: [A] Android, [i] iOS, [A/i] both.

Each section ends with a coverage table mapping every checklist line (with its platform tag) to a test + a report-ready finding. Run MobSF first for a fast automated baseline, then verify by hand.
Test your own builds or a scoped engagement, on a device/emulator you control. Rooting/jailbreaking and instrumentation are for authorised assessment only.
# Pull + decompile (Android)
adb shell pm path com.target ; adb pull /data/app/.../base.apk
jadx -d out base.apk ; apktool d base.apk -o smali_out
# Automated baseline (both platforms)
mobsf            # upload APK/IPA -> static + some dynamic
# Dynamic instrumentation
frida-ps -Ua ; objection -g com.target explore

0   Recon, static prep & tooling

Decompile and read the binary first. Hardcoded secrets, debug flags and known-vuln SDKs fall out immediately.

Static secrets & debug flags

# Secrets / endpoints / keys in code
grep -rEi 'api[_-]?key|secret|password|token|http://|https://|firebaseio|s3\.amazonaws' out/
# Android manifest red flags
grep -E 'android:debuggable|allowBackup|exported|usesCleartextTraffic' out/resources/AndroidManifest.xml
# iOS: entitlements + strings
codesign -d --entitlements - App.app ; strings App.app/App | grep -Ei 'key|secret|http'
⚑ Report as: “Hardcoded secret/endpoint in mobile binary / debuggable release build”
🛡 Fix: Keep secrets server-side, not in the binary; ship release builds with debuggable/get-task-allow off; remove debug strings and source maps; pin dependency versions and patch vulnerable SDKs; verify app integrity at runtime.

Recon & static prep — full coverage

Checklist itemHow to testReport as
[A] APK decompiled, manifest/smali reviewedjadx + apktoolStatic review baseline
[i] IPA extracted, class-dump/Ghidra reviewclass-dump / HopperStatic review baseline (iOS)
[A/i] Hardcoded secrets/endpointsgrep codeHardcoded secret
[A/i] Hardcoded crypto keys/IV/saltgrep crypto constantsHardcoded crypto material
[A/i] SDK with known CVEdependency checkVulnerable SDK
[A/i] Debug strings/internal URLsstrings/grepDebug info in binary
[A] Firebase/GCS/S3 endpoints → open storegrep + test bucketOpen backend store
[A] BuildConfig.DEBUG true in releaseinspect BuildConfigDebug flag in release
[A] android:debuggable=truemanifest checkDebuggable release
[i] get-task-allow in releaseentitlements checkDebug entitlement in release
[A/i] Source maps/symbols shippedinspect packageSymbols shipped
[A] Janus / v1-only signatureapksigner verifyWeak signature scheme
[A/i] Accepts repackaged binaryresign + runNo integrity check

1   Data storage (MASVS-STORAGE)

MASVS-STORAGE: sensitive data written to insecure local stores. Exercise the app, then inspect its sandbox.

# Android: dump app sandbox and grep for sensitive data
adb shell run-as com.target ls -R /data/data/com.target
adb pull /data/data/com.target/shared_prefs ; cat *.xml      # plaintext tokens?
adb logcat | grep -i com.target                              # secrets in logcat?
# iOS (objection): inspect Keychain / files
objection -g com.target explore
  ios keychain dump ; env ; ios nsuserdefaults get
⚑ Report as: “Sensitive data stored in plaintext (SharedPreferences / NSUserDefaults / SQLite)”
🛡 Fix: Store secrets in the Android Keystore / iOS Keychain (Keychain with WhenUnlockedThisDeviceOnly); never log sensitive data; disable allowBackup / exclude from iCloud; clear tokens on logout; suppress sensitive screens from the task-switcher snapshot; disable keyboard cache for sensitive fields.

Data storage — full coverage

Checklist itemHow to testReport as
[A] Sensitive in SharedPreferencescat shared_prefsPlaintext SharedPreferences
[A] Sensitive in SQLitepull & open DBUnencrypted SQLite
[A] Sensitive in external storagecheck /sdcardWorld-readable storage
[A] Sensitive in logcatadb logcatSensitive data in logs
[A] allowBackup=true leakadb backupBackup data leak
[i] Sensitive in NSUserDefaults/plistobjection nsuserdefaultsPlaintext NSUserDefaults
[i] Unencrypted Core Data/SQLiteinspect filesUnencrypted iOS DB
[i] Keychain weak accessibilitykeychain dumpWeak Keychain accessibility
[i] Keychain not ThisDeviceOnlyinspect attrsKeychain accessibility too broad
[i] Data to iCloud/iTunes backupcheck backup attrsSensitive data in backup
[A/i] Creds not cleared on logoutlogout, re-inspectCredentials persist after logout
[A/i] Sensitive in cache/temp/WebViewinspect cachesSensitive data cached
[A/i] Sensitive in crash/analyticsinspect payloadsSensitive data in analytics
[A/i] Clipboard leaks (OTP/card)check clipboardClipboard leak
[A/i] Screenshot/snapshot leaks screenbackground app, check snapshotTask-switcher snapshot leak
[i] Snapshot in /Library/Caches/Snapshotsinspect snapshotsiOS snapshot leak
[A/i] Keyboard cache stores inputinspect keyboard cacheKeyboard cache leak
[A/i] Autofill exposes fieldstest autofillInsecure autofill

2   Cryptography (MASVS-CRYPTO)

MASVS-CRYPTO: weak algorithms, static keys/IVs and non-CSPRNG. Find usage in the decompiled code.

⚑ Report as: “Weak/insecure cryptography (static key / ECB / MD5-SHA1 / non-CSPRNG)”
🛡 Fix: Use vetted algorithms (AES-GCM, SHA-256+); random per-message IVs; keys in Keystore/Secure Enclave, never hardcoded; CSPRNG (SecureRandom); proper KDF (PBKDF2/Argon2) with salt; no home-grown crypto.

Cryptography — full coverage

Checklist itemHow to testReport as
[A/i] Weak algorithm (DES/RC4/MD5/SHA1)grep crypto usageWeak cryptographic algorithm
[A/i] ECB modegrep "ECB"ECB mode usage
[A/i] Hardcoded/static keygrep key constantsHardcoded encryption key
[A/i] Static/predictable IVgrep IVStatic IV
[A/i] Insecure randomgrep Math.random/Random/randInsecure randomness
[A/i] Weak KDFreview key derivationWeak key derivation
[A] Key not in Keystorereview key storageKey not in Android Keystore
[i] Key not in Secure Enclave/Keychainreview key storageKey not in Secure Enclave
[A/i] Home-grown cryptoreview crypto codeCustom cryptography
[A/i] Hash without saltreview hashingUnsalted hash
[A/i] Keys retrievable from memoryfrida memory dumpKey recoverable from memory

3   Authentication & session (MASVS-AUTH)

MASVS-AUTH: client-side auth and biometric trust. The killer is a biometric/PIN result trusted client-side with no crypto binding — hookable with Frida.

# Biometric bypass — force the success callback (Android)
objection -g com.target explore
  android hooking watch class_method com.target.BiometricCallback.onAuthenticationSucceeded
# or a Frida script overriding evaluatePolicy (iOS) / onAuthenticationSucceeded (Android)
frida -U -f com.target -l bypass-biometric.js
⚑ Report as: “Biometric bypass — result trusted client-side (no crypto binding)”
🛡 Fix: Bind biometric/PIN success to a Keystore/Keychain-protected key operation (CryptoObject / SecAccessControl), not a boolean; enforce auth and 2FA server-side; rate-limit PINs server-side; revocable, short-lived tokens; step-up auth for sensitive actions.

Authentication & session — full coverage

Checklist itemHow to testReport as
[A/i] Auth validated client-side onlyhook/inspectClient-side authentication
[A/i] Token stored insecurelysee §1Insecure token storage
[A/i] Token not invalidated server-sidereuse after logoutServer-side logout missing
[A/i] Biometric result trusted client-sideFrida hook (above)Biometric bypass
[A] onAuthenticationSucceeded patchableobjection hookPatchable biometric callback
[i] evaluatePolicy patchableFrida hookPatchable LocalAuthentication
[A/i] PIN brute force (no lockout)brute PINPIN brute force
[A/i] PIN stored locally/comparableinspect storageLocal PIN comparison
[A/i] Remember-me long-lived/non-revocableinspect tokenPersistent remember-me token
[A/i] OTP autofill leakscheck clipboard/notificationOTP autofill leak
[A/i] 2FA not enforced at backendcall API post-factor-12FA not enforced server-side
[A/i] JWT issuessee API checklistJWT weakness
[A/i] Step-up auth missingdo sensitive actionMissing step-up auth

4   Network communication (MASVS-NETWORK)

MASVS-NETWORK: TLS and certificate pinning. The test is can you MITM — and if pinned, can you bypass the pin.

# Proxy traffic; if pinned, bypass with objection/frida
objection -g com.target explore
  android sslpinning disable        # (ios sslpinning disable on iOS)
# or universal Frida script
frida -U -f com.target -l frida-multiple-unpinning.js
# Then read traffic in Burp; flag cleartext, weak TLS, accepted invalid certs
⚑ Report as: “No certificate pinning / cert pinning trivially bypassable”
🛡 Fix: Enforce TLS 1.2+ with certificate (or public-key) pinning that's hard to bypass; reject self-signed/invalid certs; no permissive TrustManager/hostnameVerifier; no cleartext (set cleartextTrafficPermitted=false / ATS on); keep sensitive data out of URLs.

Network communication — full coverage

Checklist itemHow to testReport as
[A/i] Cleartext HTTP allowedproxy & observeCleartext traffic
[A] cleartextTrafficPermitted truemanifest/NSC checkCleartext permitted (Android)
[i] NSAllowsArbitraryLoads trueInfo.plist ATS checkATS disabled (iOS)
[A/i] No certificate pinningMITM succeedsNo certificate pinning
[A/i] Pinning bypassableobjection/frida bypassWeak certificate pinning
[A/i] Accepts self-signed/invalid certpresent bad certBroken TLS validation
[A/i] TrustManager/verifier accepts allreview codeTrust-all TLS
[A/i] Weak TLS version/cipherinspect handshakeWeak TLS
[A/i] Sensitive data in URL/queryinspect requestsSensitive data in URL
[A/i] MITM exposes full trafficMITM testNo transport defense-in-depth
[A/i] WebSocket unencrypted/no origininspect WSInsecure WebSocket

5   Platform interaction (MASVS-PLATFORM)

MASVS-PLATFORM: exported components, deep links and WebView. Exported components let other apps invoke your code; WebView bridges JS to native.

# Android: enumerate & invoke exported components
grep -B2 'android:exported="true"' out/resources/AndroidManifest.xml
adb shell am start -n com.target/.SecretActivity            # exported Activity
adb shell content query --uri content://com.target.provider # exported ContentProvider
drozer console connect          # then run app.provider.* / app.activity.* modules
# WebView red flags
grep -rE 'addJavascriptInterface|setAllowFileAccess|setJavaScriptEnabled' out/
⚑ Report as: “Exported component invokable by any app / WebView exposes native via addJavascriptInterface”
🛡 Fix: Export only what must be, with signature-level permissions; validate all incoming intent/URI data; immutable PendingIntents; validate deep-link/Universal-Link params and allowlist WebView navigation; avoid addJavascriptInterface with untrusted content; disable file access in WebView.

Platform interaction — full coverage

Checklist itemHow to testReport as
[A] Exported Activity launchableam startExported Activity
[A] Exported Service invokableinvoke serviceExported Service
[A] Exported BroadcastReceiversend broadcastExported Receiver
[A] Exported ContentProvider r/wcontent query/insertExported ContentProvider
[A] ContentProvider SQLi/traversalinject in provider queryContentProvider injection
[A] Implicit intent leaks datareview intentsImplicit-intent leak
[A] Intent redirection → privesccraft forwarded intentIntent redirection
[A] Mutable PendingIntent hijackreview PendingIntent flagsMutable PendingIntent
[A] Deep link param injectionadb start deep linkDeep-link injection
[A] Deep link → WebView arbitrary URLdeep link to WebViewWebView open-redirect
[A] Weak custom permission protectionLevelreview manifestWeak custom permission
[A] Task hijacking / StrandHoggreview taskAffinity/launchModeTask hijacking
[A] setResult/returned intent spoofablereview result handlingSpoofable result intent
[i] Custom URL scheme param injectionopen custom schemeURL-scheme injection
[i] Weak Universal Link validationtest universal linkUniversal-Link hijack
[i] General pasteboard sharedinspect pasteboardPasteboard leak
[i] App/share extension data leakreview extensionsExtension data leak
[i] Insecure inter-app commsreview IPCInsecure IPC
[A/i] WebView JS enabled w/ untrusted contentreview WebViewUnsafe WebView JS
[A] addJavascriptInterface exposes nativegrep + test bridgeJS bridge exposure
[A] setAllowFileAccess / file://review WebViewWebView file access
[A] setAllowUniversalAccessFromFileURLsreview WebViewUniversal file access
[i] WKWebView allowFileAccessFromFileURLsreview WebViewWKWebView file access
[A/i] XSS in WebView → bridge abuseinject XSSWebView XSS to native
[A/i] Mixed content / untrusted URLreview loadsMixed/untrusted content
[A/i] No URL allowlist on navigationnavigate off-domainMissing WebView allowlist

6   Resilience / anti-reversing (MASVS-RESILIENCE)

MASVS-RESILIENCE: anti-reversing controls. Defense-in-depth, not core vulns — severity depends on context (high for banking).

⚑ Report as: “No / trivially-bypassable root-jailbreak detection”
🛡 Fix: Layer root/jailbreak, anti-debug, anti-hook and integrity checks (not a single bypassable check); obfuscate; verify signature at runtime; keep sensitive logic server-side. Weight these higher for banking/fintech.

Resilience / anti-reversing — full coverage

Checklist itemHow to testReport as
[A/i] No root/jailbreak detectionrun on rooted deviceNo root/JB detection
[A/i] Detection trivially bypassableobjection/frida bypassBypassable root/JB detection
[A/i] No anti-debuggingattach debuggerNo anti-debug
[A/i] No anti-hookingattach FridaNo anti-hooking
[A/i] No code obfuscationreview decompiled clarityNo obfuscation
[A/i] No runtime integrity checktamper & runNo tamper detection
[A/i] No emulator detectionrun on emulatorNo emulator detection
[A] Repackaging possibleresign + runRepackaging possible
[A/i] Sensitive logic client-sidereview logic locationSensitive logic on client

7   Privacy & permissions (MASVS-PRIVACY)

MASVS-PRIVACY: over-collection and leakage of PII to third parties.

⚑ Report as: “PII over-collected / sent to third-party SDK without consent”
🛡 Fix: Request only needed permissions; disclose and get consent for PII; minimise data sent to third-party SDKs; don't misuse device identifiers; strip EXIF from uploads.

Privacy & permissions — full coverage

Checklist itemHow to testReport as
[A/i] Over-requested permissionsreview manifest/plistExcessive permissions
[A/i] PII without consentreview data flowsPII collected without consent
[A/i] PII to third-party SDKsinspect trafficPII shared with trackers
[A/i] Device identifiers misusedreview usageDevice identifier misuse
[A/i] Location collected unnecessarilyreview location useUnnecessary location collection
[A/i] Sensitive data to analytics/crashinspect payloadsSensitive data to third party
[A/i] EXIF/metadata in uploadsexiftool on uploadsMetadata leak

8   Runtime / dynamic

Cross-cutting dynamic attacks: hook the app to change auth/payment outcomes, edit local values, and tamper responses.

# Hook a method to flip an outcome (e.g. isPremium / verifyPin)
frida -U -f com.target -l hook.js
# hook.js: Java.use('com.target.Auth').verifyPin.implementation = function(){ return true; }
# Response tampering: edit the app's API response in Burp (success:false -> true)
⚑ Report as: “Method hooking changes auth/payment outcome / local value tamper alters logic”
🛡 Fix: Never trust the client for security decisions; enforce auth, limits, price and flags server-side; treat hooking/rooting as expected and validate everything server-side; consider attestation (Play Integrity / DeviceCheck) as one signal.

Runtime / dynamic — full coverage

Checklist itemHow to testReport as
[A/i] Hooking changes auth/paymentFrida PoCClient-side decision bypass
[A/i] Sensitive values in memoryfrida memory scanSensitive data in memory
[A/i] Local data tamper alters logicedit local storeClient data tampering
[A/i] Response tamper acceptedflip response in BurpResponse tampering accepted
[A/i] Local price/balance/flag editableedit & observeClient-side value trusted
[A] Logcat leaks runtime dataadb logcatRuntime log leak
[A/i] Works normally on rooted/JB for sensitive opsrun sensitive flow rootedNo root/JB enforcement on sensitive flow
Category-specific checks

A   Banking / fintech mobile

Banking weights storage, pinning, biometric/PIN, root detection and device binding as high — and the backend transaction integrity (pair with the API checklist).

⚑ Report as: “Banking: biometric/PIN bypass via hooking → unauthorised transaction”
🛡 Fix: Crypto-bound biometrics; server-enforced limits + transaction signing; certificate pinning; strong root/JB blocking on sensitive flows; device binding; no card/PIN/KYC stored on device; suppress sensitive snapshots; session timeout + kill on background.

Banking / fintech mobile — full coverage

Checklist itemHow to testReport as
[A/i] Card/CVV/account stored on deviceinspect storageFinancial data on device
[A/i] MPIN/TPIN stored/comparableinspect storageLocal PIN comparison
[A/i] MPIN brute (no server lockout)brute PINPIN brute force
[A/i] OTP autofill leakscheck clipboard/notificationOTP leak
[A/i] Biometric/PIN bypass → txnFrida hookAuth bypass to transaction
[A/i] No cert pinning on banking APIMITMNo certificate pinning
[A/i] Pinning bypass → interceptionobjection bypassPinning bypass
[A/i] Amount/beneficiary tamperabletamper requestTransaction tampering
[A/i] Local transaction limit onlyexceed via APIClient-side limit
[A/i] Device binding bypassmove sessionDevice-binding bypass
[A/i] No root/JB block on banking flowrun rootedMissing root enforcement
[A/i] Snapshot leaks account/txnbackground appSnapshot leak
[A/i] Sensitive data in logs/crashinspect logsSensitive log leak
[A/i] KYC cached unencryptedinspect storageKYC stored insecurely
[A/i] Session not killed on background/timeoutbackground & resumeMissing session timeout

B   E-commerce mobile

Price/payment tampering and BOLA via the mobile API — the app is just the entry point.

⚑ Report as: “E-commerce mobile: price/total tamperable in checkout request”
🛡 Fix: Recompute totals server-side; verify signed payment callbacks; enforce coupon/loyalty rules server-side; apply BOLA checks to orders/addresses; don't cache payment methods insecurely.

E-commerce mobile — full coverage

Checklist itemHow to testReport as
[A/i] Price/total tamperabletamper requestPrice tampering
[A/i] Coupon/loyalty client-sidetamper logicClient-side commerce logic
[A/i] Payment callback tamperforge callbackPayment bypass
[A/i] Stored payment method cachedinspect storageInsecure payment cache
[A/i] Order/wishlist/address BOLAswap IDs via APIBOLA via mobile API
[A/i] Refund/cancel tampertamper flowRefund/cancel manipulation
[A/i] Deep link to checkout w/ paramscraft deep linkDeep-link parameter injection

C   SaaS / enterprise mobile

Enterprise mobile: tenant isolation, token caching/scope, and OAuth handled over insecure URL schemes.

⚑ Report as: “SaaS mobile: OAuth token leak via custom URL-scheme hijack”
🛡 Fix: Use app-bound redirect (Universal/App Links + PKCE), not hijackable custom schemes; scope and revoke tokens on logout/device removal; derive tenant server-side; don't cache cross-tenant data offline.

SaaS / enterprise mobile — full coverage

Checklist itemHow to testReport as
[A/i] Cross-tenant via tamperable tenant IDswap tenant via APICross-tenant access
[A/i] Token cached insecurely/over-scopedinspect storageInsecure/over-scoped token
[A/i] Token not revoked on logout/removalreuse after logoutToken not revoked
[A/i] SSO/OAuth via insecure URL schemereview redirectInsecure OAuth redirect
[A/i] OAuth token leak via scheme hijackregister colliding schemeURL-scheme hijack
[A/i] MDM/work-profile data to personalreview data boundaryWork/personal data leak
[A/i] Offline cache exposes other-tenant datainspect cacheOffline cross-tenant cache

D   Healthcare mobile

PHI on the device: unencrypted caches, world-readable/iCloud-synced reports, and snapshot/log leaks.

⚑ Report as: “Healthcare mobile: PHI cached unencrypted on device”
🛡 Fix: Encrypt PHI at rest (Keystore/Keychain-backed); exclude from backups/iCloud; never log PHI; suppress sensitive snapshots; enforce per-patient authz via API; enforce consent.

Healthcare mobile — full coverage

Checklist itemHow to testReport as
[A/i] PHI cached unencryptedinspect storagePHI stored insecurely
[A/i] Reports in world-readable/iCloud storageinspect storagePHI in insecure storage
[A/i] PHI in logs/analytics/crashinspect logs/payloadsPHI in logs
[A/i] Snapshot leaks medical databackground appSnapshot leak (PHI)
[A/i] BOLA on patient records via APIswap IDsBOLA on PHI
[A/i] No consent/access enforcementaccess without consentConsent/access gap

✓   Coverage map & how to run it

Run sections 0–8 per platform tag (Android block on Android, iOS block on iOS), then the category block. Weight resilience higher for banking.

SectionRun onFocus
Universal 0–8Every mobile targetStatic, storage, crypto, auth, network, platform, resilience, privacy, runtime
BankingFintech appsStorage, pinning, biometric/PIN, root detection, device binding
E-commerceShopping appsPrice/payment tamper, BOLA via API
SaaSEnterprise appsTenant isolation, token caching, OAuth scheme hijack
HealthcareHealth appsPHI storage, snapshot/log leaks, BOLA

The reminder that pays off: a mobile pentest is static + dynamic + traffic + backend. The app is a client — most real damage is on the API it calls, so always pair this with the API checklist. Tick a box only when you've actually run the test on a device you control.

Reactions

Related Articles