diff --git a/MOBILE_CLIENT/ANDROID/_LOW/APK_MISSING_FLAG_SECURE/context.md b/MOBILE_CLIENT/ANDROID/_LOW/APK_MISSING_FLAG_SECURE/context.md new file mode 100644 index 00000000..7cafee03 --- /dev/null +++ b/MOBILE_CLIENT/ANDROID/_LOW/APK_MISSING_FLAG_SECURE/context.md @@ -0,0 +1,18 @@ +Imagine a healthcare or financial application that carefully protects its main screen with `FLAG_SECURE` so that screenshots and screen recordings of account balances, messages or a live video visit come out black. The same application also has a *second* `Activity` — a lock-screen incoming-call window that lights up the screen when a provider or caller rings, showing the caller's name and identifier right on the lock screen. Because `FLAG_SECURE` is a per-window flag and that second window never sets it, a co-resident application's `MediaProjection` session (started after the user taps "Start now" on the consent dialog), the device's stock screen recorder, or a screenshot taken while the incoming-call UI is visible captures the caller's name and identifier — sensitive, individually identifiable health or personal data — in plain text, on the very same production build that protects the main screen. + +### Why it happens + +* **`FLAG_SECURE` is per-Window, not per-app.** Setting it on one `Activity`'s window does not protect any other `Activity`'s window, even within the same process. +* **No shared base class.** When every `Activity` extends a framework class (such as `ComponentActivity`) directly, the flag cannot be inherited; each sensitive `Activity` must set it explicitly, and it is easy to forget a secondary window such as a lock-screen, notification-triggered or exported `Activity`. +* **Lock-screen windows are easy to miss.** Lock-screen `Activity` windows typically focus on `setShowWhenLocked(true)`, `setTurnScreenOn(true)` or `FLAG_KEEP_SCREEN_ON | FLAG_ALLOW_LOCK_WHILE_SCREEN_ON` to display over the keyguard, and `FLAG_SECURE` is simply never added to that code path. +* **Build guards hide one variant.** Gating the flag on `if (!BuildConfig.DEBUG)` leaves at least the `debug` build variant unprotected and signals that screen capture is intentionally allowed on some variant — a risky stance for any window that renders sensitive data. + +### Real-world impact + +* **PHI / PII exposure.** A lock-screen incoming-call UI that renders a provider/caller name and identifier exposes individually identifiable health information (the identity of a care provider and the fact that a care encounter is occurring), which is HIPAA-class data in a healthcare context. +* **No remote vector, but no attach required either.** Exploitation is not network-reachable and requires an external capture mechanism (the user's own recorder/screenshot, or a co-resident app's `MediaProjection` session after user consent) triggered while the sensitive window is on screen. On the non-debuggable production release build, capture succeeds with no debugger attach, frida or rooted device required. +* **Transient but real.** The sensitive window (for example an incoming-call lock-screen UI) is transient, which limits the capture window, but the exposed data is sensitive and the gap is present on every build variant including the shipped production build. + +### Business impact + +For organizations operating in healthcare, financial or other regulated sectors, the consequences can include regulatory exposure (HIPAA / GDPR / CCPA), reputational damage from a privacy incident, and loss of user trust. Applying `FLAG_SECURE` unconditionally to every sensitive-data window is a low-cost, high-value hardening control. diff --git a/MOBILE_CLIENT/ANDROID/_LOW/APK_MISSING_FLAG_SECURE/description.md b/MOBILE_CLIENT/ANDROID/_LOW/APK_MISSING_FLAG_SECURE/description.md new file mode 100644 index 00000000..7716071b --- /dev/null +++ b/MOBILE_CLIENT/ANDROID/_LOW/APK_MISSING_FLAG_SECURE/description.md @@ -0,0 +1,21 @@ +`FLAG_SECURE` (`WindowManager.LayoutParams.FLAG_SECURE`) is a **per-`Window`** flag. When it is set on an `Activity`'s `Window`, the Android platform (WindowManager / SurfaceFlinger) returns a black/empty bitmap for that window to `MediaProjection`, `screencap` (e.g. `adb shell screencap`, `screenrecord`) and any `SurfaceView`-mirroring / `VirtualDisplay` consumer, blocks or blackens screenshots taken with the hardware screenshot key combination or screenshot APIs, and hides the window's content in the recent-apps / overview (Recents) thumbnail. + +Because the flag is **per-Window**, an `Activity` window that does **not** set it remains fully capturable even when another `Activity`'s window in the same application sets it. Coverage must therefore be evaluated independently for every `Activity` window that renders sensitive data — there is no inheritance: each `Activity` extends a framework base class (for example `androidx.activity.ComponentActivity`) directly, so a flag applied in one `Activity`'s `onCreate` does not propagate to any other `Activity`. + +This vulnerability is raised when an `Activity` that displays sensitive data (for example a lock-screen / incoming-call / telehealth window that renders a caller or provider name and identifier, or any window presenting PHI, PII, credentials, financial or care-team data) never calls `window.setFlags(FLAG_SECURE, FLAG_SECURE)` (nor `window.addFlags(FLAG_SECURE)`) on any code path, including the production release build. Such a window is capturable on every build variant — including a non-debuggable, release-signed production build shipped via Google Play — by an external capture mechanism: the device's own stock screen recorder or screenshot key combination, `adb shell screencap` on a debuggable build, or a `MediaProjection` session started by a co-resident application after the user grants consent. No rooted device, debugger attach, or frida instrumentation is required. + +The exposure is often introduced when an application protects its main `Activity` with `FLAG_SECURE` but forgets to protect a *separate* `Activity` window — for instance a lock-screen `Activity` declared in the manifest with `android:showOnLockScreen="true"` and `android:exported="true"` that draws sensitive content into its own `ComponentActivity` window via `setContent {}`. That separate window only receives unrelated flags such as `FLAG_KEEP_SCREEN_ON` and `FLAG_ALLOW_LOCK_WHILE_SCREEN_ON` (or uses `setShowWhenLocked(true)` / `setTurnScreenOn(true)` on Android O_MR1+) and never receives `FLAG_SECURE`. + +A common, related manifestation is gating `FLAG_SECURE` behind a debug guard: + +=== "Kotlin" + ```kotlin + if (!BuildConfig.DEBUG) { + window.setFlags( + WindowManager.LayoutParams.FLAG_SECURE, + WindowManager.LayoutParams.FLAG_SECURE, + ) + } + ``` + +While AGP only sets `BuildConfig.DEBUG = true` for the literal `debug` build type (so this guard still protects custom build types such as `staging`, `prod` and `release`), the guard leaves the `debug` build variant unprotected and signals an intent to allow screen capture on at least one variant. Any `Activity` that renders sensitive data should set `FLAG_SECURE` unconditionally rather than gating it on the build variant. diff --git a/MOBILE_CLIENT/ANDROID/_LOW/APK_MISSING_FLAG_SECURE/meta.json b/MOBILE_CLIENT/ANDROID/_LOW/APK_MISSING_FLAG_SECURE/meta.json new file mode 100644 index 00000000..cbd8fc78 --- /dev/null +++ b/MOBILE_CLIENT/ANDROID/_LOW/APK_MISSING_FLAG_SECURE/meta.json @@ -0,0 +1,40 @@ +{ + "risk_rating": "low", + "short_description": "An Activity window that displays sensitive data does not set the FLAG_SECURE window flag, allowing its content to be captured by screenshots, screen recording or MediaProjection.", + "references": { + "Android Developer FLAG_SECURE": "https://developer.android.com/reference/android/view/WindowManager.LayoutParams#FLAG_SECURE", + "Android Developer Window setFlags": "https://developer.android.com/reference/android/view/Window#setFlags(int,%20int)", + "Android Developer setShowWhenLocked": "https://developer.android.com/reference/android/app/Activity#setShowWhenLocked(boolean)", + "Android Developer activity element (showOnLockScreen / exported)": "https://developer.android.com/guide/topics/manifest/activity-element", + "Android Developer MediaProjection": "https://developer.android.com/reference/android/media/projection/MediaProjection", + "CWE-200: Exposure of Sensitive Information to an Unauthorized Actor": "https://cwe.mitre.org/data/definitions/200.html", + "OWASP MASVS-STORAGE-2": "https://mas.owasp.org/MASVS/controls/MASVS-STORAGE-2/", + "OWASP MASTG-BEST-0016: Use SECURE_FLAG to Prevent Screenshots and Screen Recording": "https://mas.owasp.org/MASTG/best-practices/MASTG-BEST-0016/" + }, + "title": "Application window missing FLAG_SECURE screen-capture protection", + "privacy_issue": true, + "security_issue": true, + "categories": { + "OWASP_MASVS_L2": [ + "MSTG_STORAGE_9" + ], + "OWASP_MASVS_v2_1": [ + "MASVS_STORAGE_2" + ], + "OWASP_MOBILE_TOP_10": [ + "M6_2024" + ], + "HIPAA_CONTROLS": [ + "SECURITY212", + "SECURITY215" + ], + "GDPR": [ + "ART_5", + "ART_32" + ], + "SOC2_CONTROLS": [ + "CC_6_1", + "CC_7_1" + ] + } +} diff --git a/MOBILE_CLIENT/ANDROID/_LOW/APK_MISSING_FLAG_SECURE/recommendation.md b/MOBILE_CLIENT/ANDROID/_LOW/APK_MISSING_FLAG_SECURE/recommendation.md new file mode 100644 index 00000000..6e3fdf4d --- /dev/null +++ b/MOBILE_CLIENT/ANDROID/_LOW/APK_MISSING_FLAG_SECURE/recommendation.md @@ -0,0 +1,43 @@ +Add `FLAG_SECURE` to every `Activity` window that displays sensitive data, on every code path and build variant. Apply the flag in `onCreate`, before `setContent {}` draws any UI, so the protection is in place from the first rendered frame. + +### Immediate mitigation + +=== "Kotlin" + ```kotlin + override fun onCreate(savedInstanceState: Bundle?) { + showOnLockScreen() + super.onCreate(savedInstanceState) + // Block screenshots, screen recording and MediaProjection of this window on every build variant. + window.setFlags( + WindowManager.LayoutParams.FLAG_SECURE, + WindowManager.LayoutParams.FLAG_SECURE, + ) + // ... existing extras handling and setContent ... + } + ``` + +Set the flag **unconditionally** — do not gate it on `BuildConfig.DEBUG`. Keep any existing keep-screen-on / allow-lock-while-screen-on logic; `FLAG_SECURE` is additive and compatible with `FLAG_KEEP_SCREEN_ON`, `FLAG_ALLOW_LOCK_WHILE_SCREEN_ON`, `setShowWhenLocked(true)` and `setTurnScreenOn(true)`. + +### Permanent fix + +Introduce a shared base `ComponentActivity` that sets `FLAG_SECURE` in `onCreate` on every build variant, and have every sensitive-data `Activity` extend it. This removes the per-`Activity` reliance on remembering to set the flag and prevents the same gap recurring for future Activities. + +=== "Kotlin" + ```kotlin + abstract class SecureComponentActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + window.setFlags( + WindowManager.LayoutParams.FLAG_SECURE, + WindowManager.LayoutParams.FLAG_SECURE, + ) + } + } + + class IncomingCallActivity : SecureComponentActivity() { /* ... */ } + class MainActivity : SecureComponentActivity() { /* ... */ } + ``` + +### Prevent regressions + +Add an instrumentation test or a static check (lint / detekt / unit test) asserting that every `Activity` in the sensitive-data set sets `FLAG_SECURE` on its window in `onCreate` (for example assert `window.attributes.flags and FLAG_SECURE != 0`), so a new sensitive `Activity` cannot ship without the flag. A repository-wide search for `FLAG_SECURE` should return a match in every sensitive `Activity` (or the shared base class).