Back to Cheat Sheets

🐛 Client Code QualityMobile 2016

OWASP Mobile Top 10 2016 - M7

MEDIUM RISK

📋 What Is It?

Client Code Quality is the category for code-level implementation defects inside the mobile client itself that turn into security problems - memory-safety bugs, unchecked input handling, misuse of dangerous APIs, and sloppy error handling in the app you ship to the device.

The distinction matters. Most mobile risks are about what the app does with data; M7 is about how the code is written. Two apps can implement the same feature - one parses a deep link with a bounds-checked, type-safe routine, the other copies attacker-controlled bytes into a fixed native buffer with strcpy. Same feature, same data flow, but only the second crashes, corrupts memory, or executes attacker code when fed a hostile input.

M7 OWASP Rank 2016
Client Side Only
Native Where It Bites Hardest

⚠️ Common Exploits

  • Buffer Overflows: strcpy/sprintf/memcpy with an attacker-controlled length
  • Format String Bugs: user data passed as the format argument
  • Use-After-Free / Double-Free: lifetime errors in native code
  • Unsafe Deserialization: arbitrary object graphs decoded from untrusted bytes
  • Unvalidated Deep Links & IPC: intent extras and URLs trusted as-is
  • Integer Overflow: a size calculation wraps and under-allocates
  • Unhandled Errors: null dereference or uncaught exception → crash-to-DoS
  • Dangerous WebView APIs: addJavascriptInterface on legacy targets

🔴 Attack Flow

1. Map every input the client parses: deep links, IPC, files, QR, BLE/NFC, server responses
↓
2. Fuzz each with oversized, malformed, and unexpected-type values
↓
3. Watch for crashes, tombstones, and memory-corruption signatures
↓
4. Triage a crash into a controllable primitive in native code
↓
5. IMPACT: Denial of service - or code execution inside the app's sandbox!

❌ Vulnerable Code

// Bad (JNI): fixed buffer, unbounded copy void handle(const char *input) { char buf[64]; strcpy(buf, input); ← overflow whenever input > 63 bytes printf(input); ← format string: attacker controls %n/%x } // Bad: size arithmetic that can wrap int total = count * size; ← overflow → tiny allocation, big copy char *p = malloc(total); memcpy(p, src, count * size); // Bad: untrusted deep link consumed without validation val url = intent.getStringExtra("url")!! webView.loadUrl(url) ← javascript:, file://, attacker origin // Bad: arbitrary deserialization from an untrusted source val obj = ObjectInputStream(input).readObject() ← gadget chains // Bad: failures swallowed, state left inconsistent try { verifySignature(payload) } catch (e: Exception) { } ← fails open

✅ Secure Code

// Good (JNI): length-limited copy, literal format string void handle(const char *input, size_t len) { char buf[64]; if (len >= sizeof(buf)) return; // reject rather than truncate silently memcpy(buf, input, len); buf[len] = '\0'; printf("%s", buf); // data is never the format } // Good: checked arithmetic before allocating size_t total; if (__builtin_mul_overflow(count, size, &total)) return ERR; // Good: validate every deep link against an allow-list val uri = Uri.parse(intent.getStringExtra("url") ?: return) require(uri.scheme == "https" && uri.host in ALLOWED_HOSTS) webView.loadUrl(uri.toString()) // Good: parse into known types only val dto = json.decodeFromString<ProfileDto>(body) // no arbitrary classes // Good: fail closed, and prefer memory-safe languages for parsers try { verifySignature(payload) } catch (e: Exception) { abortAndReport(e); return } // build with -fstack-protector-strong, _FORTIFY_SOURCE=2, PIE, and run sanitizers in CI

✓ Prevention Checklist

  • Validate every untrusted input at the client boundary: type, length, range, format
  • Treat deep links, IPC extras, files, and server responses as hostile
  • Avoid strcpy, sprintf, gets; use length-limited APIs
  • Check arithmetic for overflow before allocating or copying
  • Deserialize into known types only; never decode arbitrary object graphs
  • Prefer memory-safe languages (Kotlin/Swift/Rust) for parsing untrusted data
  • Handle errors explicitly and fail closed - never swallow an exception
  • Enable compiler and linker hardening; run ASan/UBSan in CI
  • Fuzz every input parser as part of the build
  • Zero secrets in memory once used; avoid leaving them in long-lived buffers
  • Enforce static analysis and code review on all native code

🔍 Detection & Tools

Static & Build:

clang-tidy Infer Semgrep Android Lint MobSF

Dynamic:

AddressSanitizer libFuzzer / AFL++ Frida drozer

How to Test:

  • Fuzz deep links, intent extras, and file imports with oversized inputs
  • Send malformed server responses to the app's parsers
  • Run debug builds with ASan/UBSan and exercise every input path
  • Review all JNI/native code for unbounded copies and format strings
  • Check crash logs for repeatable, attacker-triggerable faults

🌍 Real-World

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

  • Media and image parsers: memory-corruption bugs in native decoding libraries reached by a crafted file or message attachment.
  • Deep-link handlers: unvalidated URLs loaded into WebViews leading to local file access or script execution in the app's context.
  • Deserialization gadgets: untrusted serialized data decoded into arbitrary classes, giving code execution.
  • Crash-to-DoS: a malformed push payload or IPC message that reliably kills the app on receipt.
  • Legacy WebView bridges: addJavascriptInterface on old API levels exposing reflection to remote pages.

📌 Quick Tips

  • DO NOT copy untrusted bytes into fixed buffers
  • DO NOT pass user data as a format string
  • DO NOT deserialize arbitrary types
  • DO validate every client-side input boundary
  • DO fuzz parsers and run sanitizers in CI
  • DO prefer memory-safe languages for parsing

📜 Compliance

Related Standards:

  • OWASP Mobile Top 10 2016 - M7
  • Maps to M4:2024 Insufficient Input/Output Validation
  • OWASP MASVS CODE-1 … CODE-4
  • CWE-120 Buffer Copy without Checking Size of Input
  • CWE-134 Uncontrolled Format String
  • CWE-416 Use After Free
  • CWE-502 Deserialization of Untrusted Data