auditing-android-intent-redirection-and-pendingintent · git:20260908.e3ae18a · 2026-09-08 · sha256 b94af3580f9b8d23
auditing-android-intent-redirection-and-pendingintent git:20260908.e3ae18aA
Immutable. This exact content is served forever at /api/v1/blob/b94af3580f9b8d23.
--- name: auditing-android-intent-redirection-and-pendingintent description: >- Audit Android privilege and access leaks through intent redirection and mutable pending intents, where a privileged component receives an intent carrying a nested intent it then launches, or hands out a pending intent an untrusted app can fill in, so the untrusted caller reaches a protected component or acts with the privileged app's identity, because the component forwards an attacker-supplied intent without constraint or the pending intent is mutable and under-specified. Use when an Android app forwards intents extracted from input or shares pending intents with other apps. Covers nested-intent redirection to protected components, mutable pending intents, implicit pending-intent delivery, and forwarding that carries the app's permissions. The attacker-supplied nested or fillable intent is the source, the privileged component launching or sending it is the sink, and reaching a protected target with the app's identity is the bug. license: MIT --- # Auditing Android intent redirection and PendingIntent: when an app relays an attacker's intent An Android app acts on intents, and two patterns let an untrusted caller borrow the app's privilege. In intent redirection, a component receives an intent that carries another intent inside it and then launches that nested intent; if the component is reachable by other apps and forwards the nested intent without constraint, an untrusted caller supplies an intent aimed at a protected component the caller could not reach directly, and the privileged app launches it with the app's own identity. In the pending-intent case, the app creates a token that lets another app perform an operation as the app later; if that token is mutable and under-specified, the other app fills in the missing parts, target, action, or data, and directs the operation where it wants, still under the app's identity. In both, the app relays an attacker's intent and lends its privilege to the destination. The bug is a privileged component launching or sending an intent an untrusted caller controls. You audit these by finding every forwarded and shared intent and checking what the caller can steer. ## When to use - An Android app forwards an intent it extracts from a received intent's extras or data. - The app creates pending intents shared with other apps or delivered implicitly. - A reachable component launches or sends intents on behalf of its caller. ## Scope check Audit intent redirection only on apps and devices you own or are authorized to assess, on test devices and accounts, driving the component with a benign test app you control rather than acting against real data. A confirmed redirection reaches protected components with the app's privilege, so keep every probe within scope. If you can't name the authorization, stop. ## The loop 1. **Establish whether the caller can actually steer the launched or sent intent first.** For each component that forwards a nested intent or shares a pending intent, determine whether the target and contents are fixed by the app, or whether an untrusted caller can supply or fill in the intent that is ultimately launched or sent. This is the false-positive killer: a component that launches only a fully specified, app-fixed intent, or a pending intent that is immutable and complete, cannot be steered by an attacker. Name the attacker-controlled part before crafting an intent. 2. **Enumerate forwarded and shared intents.** List the components reachable by other apps that extract an intent from input and launch it, and the pending intents the app creates and shares or delivers implicitly. Note the identity and permissions each launch or send carries. 3. **Check nested-intent redirection.** For a component that launches a nested intent taken from its input, determine whether it constrains the target, so it can only reach intended components, or forwards the attacker-supplied intent freely, letting the caller aim it at a protected component the app can reach but the caller cannot. 4. **Check pending-intent mutability and specificity.** For each shared pending intent, determine whether it is immutable and fully specifies its target, action, and data, or whether it is mutable or under-specified so the receiving app fills in the blanks and redirects the operation, and whether it is delivered implicitly where any app can intercept it. 5. **Check the identity the redirect carries.** Determine what privilege the launched or sent intent runs with, the app's own permissions, access to its protected components, or its granted data access, since the redirection matters only when it lends the caller something they lacked. 6. **Confirm and record.** Confirm by sending a benign test app's intent that redirects through the component to a protected target, or filling in a mutable pending intent to redirect it, and observing the app's privilege reach the target on a test device. Kill the lead if forwarded intents are constrained to intended targets, if shared pending intents are immutable and fully specified and not delivered implicitly, or if the redirect carries no privilege the caller lacked. Record the component, the attacker-controlled intent, the protected target, and the access observed, or set a `kill_reason`. ## Where intent redirection leaks - **The steerable intent is the finding.** An app forwarding or sharing intents is normal; the bug is an untrusted caller steering where a privileged launch or send goes. Name the attacker-controlled part. - **Unconstrained nested intents reach protected components.** A component that launches an attacker-supplied nested intent without limiting the target lets the caller reach components the app can but they cannot. - **Mutable pending intents are filled in by the receiver.** A mutable or under-specified pending intent lets the receiving app set the target, action, or data, redirecting the operation under the app's identity. - **Implicit pending-intent delivery invites interception.** A pending intent delivered implicitly can be received by any app that matches, handing an untrusted app a token that acts as the creator. - **The borrowed identity is the impact.** Redirection matters because the launch or send runs with the app's permissions and access, so the caller reaches what the app can reach, not what they can. ## Worked example (a confirm and a kill) > **Confirm.** An exported component extracts an intent from its extras and launches it without constraining > the target. A benign test app sends an intent whose nested intent targets a protected, non-exported > component of the same app, and the privileged app launches it, reaching the protected component with the > app's identity on a test device. **Confirmed** intent redirection reaching a protected component with the > app's privilege, `high`, remediation = do not launch intents extracted from untrusted input, or constrain > the forwarded target to an explicit intended component, and make shared pending intents immutable and fully > specified. > > **Kill.** The same component launches only an explicit, app-fixed intent and never forwards an > attacker-supplied nested intent, and every shared pending intent is immutable, fully specifies its target, > action, and data, and is delivered explicitly to a known package. An untrusted caller can steer neither the > nested intent nor a pending intent. **Killed**, `kill_reason` = "forwarded intents are fixed to explicit > intended targets and shared pending intents are immutable and fully specified; no untrusted caller steers a > privileged launch or send." ## Rationalizations to reject - *"The component only forwards intents."* -> Forwarding an attacker-supplied intent is the redirection; if the target is not constrained, the caller aims it at a protected component through the app. - *"The pending intent is ours."* -> A mutable or under-specified pending intent shared with another app lets that app fill in the target and act as you; make it immutable and fully specified. - *"Other apps cannot reach that component."* -> If the component is exported or otherwise reachable, another app reaches it; confirm its reachability rather than assuming it is private. - *"It is delivered to a broadcast."* -> An implicit pending intent or broadcast can be intercepted by any matching app; deliver explicitly to a known package. - *"The launched action is harmless."* -> The action runs with the app's identity and access; a harmless- looking launch can reach protected components or granted data the caller lacked. ## Executing this in practice You need every component reachable by other apps that forwards a nested intent, every pending intent the app shares or delivers implicitly, and for each the identity and permissions the launch or send carries, plus whether the target and contents are app-fixed or caller-steerable. For each, decide whether an untrusted caller can direct a privileged launch or send. Reading the forwarding and pending-intent construction settles most leads; driving the component from a benign test app to reach a protected target on a test device settles the rest. ## Related - `auditing-android-component-exposure` - the exported-component reachability this redirection depends on is the surface that skill audits, so they meet on which components an untrusted app can reach. - `auditing-mobile-deeplink-trust` - a deep link that carries a redirecting intent is one delivery path for the attacker-supplied intent this skill forwards. - `auditing-ios-app-group-and-pasteboard-exposure` - the iOS side of cross-app data reach, a companion to the Android inter-app privilege reach here. - `auditing-mobile-webview-bridge-exposure` - a redirected intent that drives a WebView to load attacker content chains into the bridge exposure that skill audits. - [FINDING-SCHEMA.md](../../FINDING-SCHEMA.md) - source = the attacker-supplied nested or fillable intent, sink = the privileged component launching or sending it, evidence = reaching a protected target with the app's identity from a benign test app on a test device.