Back to Cheat Sheets

🚪 Extraneous FunctionalityMobile 2016

OWASP Mobile Top 10 2016 - M10

MEDIUM RISK

📋 What Is It?

Extraneous Functionality covers hidden backdoors and internal or development-only functionality that was never meant for end users but ships inside the released app anyway. The code did its job during development - a test shortcut, a debug menu, a staging endpoint, a verbose log line - and nobody removed it before the build went to the store.

The defining characteristic is intent: this is not a logic mistake and not an attacker's injection. It is legitimate developer functionality that outlived its purpose and leaked into production. Because the app is a binary running on a device the attacker controls, anything compiled into it can be recovered, read, and exercised - and there is no server to quietly patch it out of reach.

M10 OWASP Rank 2016
Dev Code In Production
Easy To Find In A Binary

⚠️ Common Exploits

  • Debug Menus: hidden settings screens reachable by a gesture or code
  • Test Credentials: qa_admin / Test1234 compiled into a constant
  • Developer Backdoors: a magic value that skips a licence or auth check
  • Staging Endpoints: base URL pointing at, or switchable to, staging
  • Verbose Logging: full request/response bodies, tokens, and PII in logcat
  • Debuggable Releases: android:debuggable="true" in a shipped APK
  • Disabled-But-Present Features: gated only by a boolean the attacker can flip
  • Revealing Comments: internal architecture and TODOs describing weaknesses

🔴 Attack Flow

1. Decompile the release build and read strings, resources, and the manifest
↓
2. Search for "debug", "test", "staging", "internal", and credential-shaped constants
↓
3. Reach the hidden entry point - deep link, gesture, exported component, or flag
↓
4. Use the leftover functionality or the endpoint it points to
↓
5. IMPACT: Privileged features, internal systems, or credentials handed over!

❌ Vulnerable Code

// Bad: a backdoor that survived to release if (username == "qa_admin" && password == "Test1234") { grantAdminAccess() ← works in production too } // Bad: hidden debug menu, one gesture away if (tapCount == 7) startActivity(Intent(this, DebugMenuActivity::class.java)) // exposes feature toggles, raw API console, and account switching // Bad: environment switch shipped to users val BASE_URL = if (prefs.getBoolean("use_staging", false)) "https://staging-api.example.com" else "https://api.example.com" <!-- Bad: debuggable release --> <application android:debuggable="true" /> // Bad: verbose logging left on Log.d("NET", "req=" + body + " auth=" + token) ← readable by tooling // TODO: remove before release - apiKey = "sk_live_…" ← still in the source, still shipped

✅ Secure Code

// Good: development code cannot exist in a release build if (BuildConfig.DEBUG) { installDebugMenu() // R8 strips this branch entirely from release } // Good: no environment switching in the shipped app val BASE_URL = BuildConfig.API_BASE_URL // compiled per flavour; release = production // Good: debug-only source set - the code is not in the release artifact at all // src/debug/java/…/DebugMenuActivity.kt (never compiled into release) <!-- Good: release manifest --> <application android:debuggable="false" /> // Good: logging disabled in release, secrets never logged at all if (BuildConfig.DEBUG) Log.d("NET", "request ok") // proguard-rules.pro: -assumenosideeffects class android.util.Log { public static *** d(...); } # Good: CI gates that make this un-shippable # - grep the built artifact for debug/test/staging markers and credential patterns # - fail the build on any hit; scan with gitleaks/MobSF before publishing

✓ Prevention Checklist

  • Keep debug and test code in debug-only source sets and build flavours
  • Never ship test accounts, magic values, or bypass logic
  • Compile environment URLs per flavour; no runtime environment switch for users
  • Set debuggable=false and strip verbose logging from releases
  • Remove hidden menus, gestures, and internal tooling from production builds
  • Do not gate a shipped feature with a client-side boolean alone
  • Scan the built artifact - not just the source - for debug and secret markers
  • Fail CI on any hit; make the release pipeline the enforcement point
  • Review the manifest, resources, and strings before every release
  • Rotate any credential that ever appeared in a shipped build

🔍 Detection & Tools

Artifact Inspection:

jadx / apktool MobSF strings aapt dump

CI Gates:

gitleaks trufflehog Semgrep Custom release checks

How to Test:

  • Decompile the release build and grep for debug, test, staging, internal
  • Enumerate exported components and deep links for hidden screens
  • Look for credential-shaped constants and internal hostnames
  • Check the manifest for debuggable and unnecessary permissions
  • Watch logs during normal use for verbose output that should not be there

🌍 Real-World

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

  • Hidden debug menus in shipped apps: a gesture or code opens internal tooling with feature toggles and account switching.
  • Test accounts in production: hardcoded QA credentials that still authenticate against live systems.
  • Staging endpoints exposed: a switch, or a leftover default, sends users' apps at less-protected internal environments.
  • Debuggable release builds: published apps that allow a debugger to attach and inspect or modify runtime state.
  • Verbose logging left on: tokens and PII written to system logs where other tooling can collect them.

📌 Quick Tips

  • DO NOT ship test credentials or bypass logic
  • DO NOT leave debug menus or debuggable flags in a release
  • DO NOT offer users an environment switch
  • DO isolate debug code in debug-only source sets
  • DO scan the built artifact in CI and fail on hits
  • DO rotate anything that ever shipped as a secret

📜 Compliance

Related Standards:

  • OWASP Mobile Top 10 2016 - M10
  • Maps to M8:2024 Security Misconfiguration
  • OWASP MASVS RESILIENCE-2, CODE-2
  • CWE-489 Active Debug Code
  • CWE-798 Use of Hard-coded Credentials
  • CWE-1244 Internal Asset Exposed to Unsafe Debug Access Level