Back to Cheat Sheets

🔨 Code TamperingMobile 2016

OWASP Mobile Top 10 2016 - M8

MEDIUM RISK

📋 What Is It?

Code Tampering is an attacker modifying a mobile application's binary, its resources, or its behaviour in memory on a device they control - then running the altered app to make it do something the developer never intended. The app executes on hardware the attacker fully owns, so every byte of code and every value in RAM is reachable and rewritable.

It covers repackaging (decompile, patch, resign, redistribute a trojanized app), binary patching (flip one licence, root, or "premium" check), resource modification, and runtime manipulation (hooking methods, editing memory, rewriting return values). The unifying idea: the shipped artifact is not the artifact that runs.

M8 OWASP Rank 2016
Attacker Owns The Runtime
Server Is The Only Referee

⚠️ Common Exploits

  • Repackaging: decompile → patch smali → resign → distribute a lookalike
  • Check Removal: licence, root, or subscription tests NOP-ed out
  • Runtime Hooking: Frida/Xposed rewriting method return values
  • Memory Editing: in-game currency, balances, and flags changed live
  • Resource Swaps: endpoints, certificates, or assets replaced
  • Anti-Tamper Bypass: the integrity check itself patched out first
  • Response Rewriting: server answers modified in transit or in RAM

🔴 Attack Flow

1. Pull the APK/IPA from the device or a mirror
↓
2. Decompile and locate the check to defeat
↓
3. Patch the bytecode, or hook the method at runtime
↓
4. Resign and reinstall, or run the hook against the stock app
↓
5. IMPACT: Paid features unlocked, controls bypassed, trojanized builds shipped!

❌ Vulnerable Code

// Bad: the business decision lives entirely on the client fun isPremium(): Boolean = prefs.getBoolean("premium", false) if (isPremium()) unlockAllContent() ← one hook returns true, forever // Bad: licence validated offline, result trusted if (checkLicenseLocally(key)) enableApp() ← patch the branch, done // Bad: security state kept in an editable in-memory field var isRooted = detectRoot() ← flip the value in memory // Bad: integrity check that reports only to itself if (signatureHash() != EXPECTED) Toast.makeText(…).show() ← cosmetic, removable // Bad: server accepts whatever the client claims api.post("/purchase/complete", mapOf("verified" to true)) ← client-asserted truth

✅ Secure Code

// Good: entitlement is a server fact, re-checked on every privileged call val entitlements = api.entitlements() // short-lived, signed by the server // content is delivered only after the server authorises it - patching the UI gains nothing val content = api.fetchPremiumContent() // 403 unless genuinely entitled // Good: purchases verified server-to-server with the store // client sends the receipt; the SERVER validates it with Apple/Google and grants access api.post("/purchase/verify", mapOf("receipt" to receipt)) // Good: platform attestation, evaluated server-side val token = playIntegrityToken() // or DeviceCheck / App Attest on iOS api.post("/session", mapOf("integrity" to token)) // the server decides what an untrusted verdict means: step-up auth, limits, or refusal // Good: layered client hardening - cost, not a guarantee // R8/ProGuard + native obfuscation, checksum critical native code, // detect debuggers/hooks and report the signal to the server, then let the SERVER react reportTelemetry("integrity.suspect", signals) // server throttles or blocks the account

✓ Prevention Checklist

  • Keep every security and business decision on the server
  • Never trust a client-asserted entitlement, price, score, or verification flag
  • Verify in-app purchases server-to-server with the store
  • Use platform attestation (Play Integrity, App Attest/DeviceCheck) and judge it server-side
  • Report tamper signals to the server rather than acting only locally
  • Apply code shrinking and obfuscation to raise the cost of analysis
  • Add integrity checks in native code, in several places, that fail quietly
  • Ship release builds with debugging disabled and symbols stripped
  • Monitor app stores and mirrors for repackaged clones
  • Rate-limit and anomaly-detect on the server: tampered clients behave differently
  • Accept the premise - client hardening delays an attacker, it never stops one

🔍 Detection & Tools

Attacker Toolkit (know it):

apktool jadx Frida Xposed / LSPosed Ghidra

Defence:

Play Integrity API App Attest / DeviceCheck R8 / ProGuard Server-side anomaly detection

How to Test:

  • Repackage the app with a flipped premium or licence check and run it
  • Hook the relevant methods with Frida and force the return values
  • Confirm the server still refuses the privileged action
  • Verify integrity/attestation failures are acted on server-side, not just logged
  • Check that release builds are not debuggable and are stripped

🌍 Real-World

These are documented incident classes - recurring patterns rather than single CVEs.

  • Modded apps on third-party stores: popular apps repackaged with premium unlocked, ads stripped, and additional code added.
  • Trojanized clones: a legitimate app rebuilt with credential-stealing code and distributed through sideloading.
  • Game economy manipulation: memory editing and hooks that mint currency or items when the server trusts client-reported state.
  • Root/jailbreak detection defeated: the check hooked to return false before any of its consequences run.
  • Receipt forgery: purchase confirmations faked on the client where the server never validates with the store.

📌 Quick Tips

  • DO NOT keep security decisions on the client
  • DO NOT trust client-reported purchases or state
  • DO NOT rely on obfuscation as a control
  • DO verify entitlements and receipts server-side
  • DO use attestation and judge it on the server
  • DO watch for repackaged clones in the wild

📜 Compliance

Related Standards:

  • OWASP Mobile Top 10 2016 - M8
  • Maps to M7:2024 Insufficient Binary Protections
  • OWASP MASVS RESILIENCE-1 … RESILIENCE-4
  • CWE-494 Download of Code Without Integrity Check
  • CWE-693 Protection Mechanism Failure
  • CWE-602 Client-Side Enforcement of Server-Side Security