Intent Hijacking¶
Intercepting or redirecting intents meant for another app component. Possible when an app sends implicit intents or exports components without proper protection. The attacker's app registers to handle the same intent and receives data meant for the legitimate component.
Requirements
| Requirement | Details |
|---|---|
| Permission | None (exploits component export misconfiguration) |
| Condition | Target app uses implicit intents or exports components without restrictions |
Variants¶
Implicit Intent Interception¶
When an app sends an implicit intent (no target component specified), Android resolves it to any matching component. If a malicious app declares a matching <intent-filter>, it can receive the intent.
<activity android:name=".MaliciousActivity" android:exported="true">
<intent-filter>
<action android:name="com.target.app.CUSTOM_ACTION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
If the target app sends new Intent("com.target.app.CUSTOM_ACTION") without specifying a component, the system may route it to the malicious activity. If multiple handlers exist, the user sees a chooser dialog (which social engineering can handle).
Activity Hijacking via startActivityForResult¶
When an app starts an implicit activity expecting a result, the malicious activity can return crafted data:
- Target app calls
startActivityForResult()with implicit intent - Malicious activity catches the intent
- Malicious activity reads any extras (potentially sensitive data)
- Malicious activity returns a crafted result to influence the caller's behavior
Exported Component Abuse¶
Components declared with android:exported="true" (or with an intent filter, which implies export on API < 31) can be started by any app:
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.target.app", "com.target.app.InternalActivity"));
startActivity(intent);
This can reach activities not designed for external use: admin panels, debug screens, data viewers.
Intent Redirection¶
A more powerful variant where a vulnerable app is tricked into launching its own non-exported components on behalf of the attacker. Oversecured's systematic research found this in more than 80% of analyzed apps. The attack flow:
- Attacker sends an intent to an exported activity that accepts an "inner intent" parameter
- The exported activity extracts the inner intent and calls
startActivity()with it - Since the call originates from within the app's own process, non-exported components are reachable
This is particularly dangerous because it grants access to content providers, WebViews, and internal activities that were never designed for external interaction. Oversecured's Samsung research used this to access system-level components on Samsung devices, and their Google apps audit found widespread intent redirection across 225 Google applications.
Arbitrary Code Execution via Package Contexts¶
Oversecured documented how Android's package context resolution can be exploited for arbitrary code execution. By manipulating how Android resolves and loads code from third-party package contexts, an attacker can execute arbitrary code within the target app's process, inheriting all its permissions and data access.
Service Hijacking¶
An implicit intent to bind a service can be intercepted by a malicious service. The malicious service then receives all IPC calls meant for the legitimate service.
On Android 5.0+, implicit intents to services throw an exception, forcing explicit binding. This largely eliminates this variant on modern devices.
Ordered Broadcast Interception¶
See Broadcast Theft for broadcast-specific interception.
Real-World Impact¶
| Vulnerability Type | Example |
|---|---|
| OAuth redirect interception | Malicious app registers for the OAuth callback URI scheme, stealing authorization codes |
| Payment intent interception | Intercepting payment processing intents to redirect to attacker-controlled handlers |
| File sharing interception | Registering for ACTION_SEND to capture files shared between apps |
| Deep link hijacking | See Deep Link Exploitation |
Android Mitigations¶
| Version | Mitigation | Bypass |
|---|---|---|
| Android 5.0 (API 21) | Implicit intents to services throw IllegalArgumentException |
Does not affect activities or broadcast receivers |
| Android 12 (API 31) | Components with intent filters must explicitly declare android:exported |
Developers often set exported="true" to resolve build errors |
| Android 12+ | PendingIntent mutability must be declared (FLAG_MUTABLE / FLAG_IMMUTABLE) |
Apps requiring FLAG_MUTABLE (notifications, MediaSession) remain vulnerable if base intent is not fully specified |
Detection During Analysis¶
Static Indicators
- Implicit intents sent with sensitive data in extras
- Components with
android:exported="true"that handle sensitive operations - Activities without
android:permissionprotection that accept external input PendingIntentcreation withoutFLAG_IMMUTABLE- Custom URI schemes without proper validation
- Activities that extract and launch "inner intents" from extras (intent redirection)
Dynamic Indicators
- Exported activities reachable via
adb shell am startthat expose internal functionality - PendingIntents intercepted via notification or widget that accept modified base intents
- Implicit intents routed to unexpected handler apps
Oversecured's interception of implicit intents guide provides detailed technical coverage of implicit intent resolution mechanics and exploitation patterns.
Pending Intent Vulnerabilities¶
A PendingIntent wraps an intent and grants the recipient permission to execute it as if it came from the original sender, with the sender's identity and permissions. When a PendingIntent is created as mutable (FLAG_MUTABLE), the recipient can modify the base intent before executing it -- changing the target component, adding extras, or altering the action.
Attack Flow¶
- A privileged app (e.g., system service, banking app) creates a
PendingIntentfor a notification action, widget update, orMediaSessioncallback - The
PendingIntentis delivered to a less-privileged context -- a notification visible to all apps, a widget host, or an IPC call - The attacker's app receives or intercepts the
PendingIntent - If the
PendingIntentis mutable and was created with an implicit or empty base intent, the attacker fills in or modifies the intent fields - The attacker calls
PendingIntent.send()with a modified intent - Android executes the modified intent under the original sender's identity, with all of the sender's permissions
Why This Is Dangerous¶
The PendingIntent carries the sender's UID and permission set. When the attacker triggers it with a modified intent, the system treats it as an action by the original app. This means:
- If the sender holds
MANAGE_EXTERNAL_STORAGE, the attacker can read/write arbitrary files - If the sender is a system app, the attacker can launch non-exported system components
- If the sender holds
INTERNETand the attacker doesn't, the attacker can exfiltrate data through the sender's network access
Vulnerable Patterns¶
PendingIntent pi = PendingIntent.getActivity(
context, 0, new Intent(), PendingIntent.FLAG_MUTABLE
);
An empty base intent with FLAG_MUTABLE is the worst case. The attacker has full control over the intent that gets executed under the sender's identity.
A safer pattern uses FLAG_IMMUTABLE and a fully specified explicit intent:
Intent explicit = new Intent(context, SpecificActivity.class);
explicit.setPackage("com.myapp");
PendingIntent pi = PendingIntent.getActivity(
context, 0, explicit, PendingIntent.FLAG_IMMUTABLE
);
Research References¶
Oversecured's PendingIntent research documented this class of vulnerability across major Android apps, finding mutable PendingIntents with empty or implicit base intents in system components, OEM apps, and popular third-party applications. Google's own security bulletin has patched multiple PendingIntent issues in system services.
Android 12 (API 31) requires explicit mutability declaration (FLAG_MUTABLE or FLAG_IMMUTABLE), which forces developers to consciously decide. However, apps that need mutable PendingIntents (e.g., for inline reply in notifications, MediaSession callbacks) remain vulnerable if the base intent is not fully specified.
Families Using This Technique¶
| Family | Technique | Target |
|---|---|---|
| Anatsa | Abuses exported activities in banking apps to launch internal transfer screens | European banking apps |
| Vultur | Exploits exported components to trigger screen recording and keylogging setup | Banking and crypto apps |
| SpyNote | Launches exported activities of target apps to extract stored data and credentials | Enterprise and banking apps |
| Cerberus | Intercepts OAuth redirect intents by registering competing intent filters for custom URI schemes | Banking apps using OAuth flows |
| SharkBot | Hijacks Automated Transfer System flows by injecting intents into banking app exported components | European banking apps |
| Joker | Intercepts SMS intents and notification listeners to steal OTPs and subscription confirmations | Premium SMS services |
Testing for Intent Vulnerabilities¶
Enumerating Exported Components with Drozer¶
Drozer is the standard tool for auditing Android IPC attack surfaces. Install it on a test device or emulator and connect via ADB.
List all exported activities:
List all exported services:
List all exported broadcast receivers:
List all exported content providers:
Testing Intent Injection¶
Launch an exported activity with crafted extras:
dz> run app.activity.start --component com.target.app com.target.app.InternalActivity \
--extra string secret_data "injected_value"
Send an intent to an exported broadcast receiver:
dz> run app.broadcast.send --action com.target.app.SENSITIVE_ACTION \
--extra string token "attacker_token"
Checking for Intent Redirection¶
Search the decompiled source for activities that extract an intent from extras and launch it:
This pattern allows an attacker to send any intent as the next_intent extra, and the vulnerable app will launch it from its own process, reaching non-exported components.
PendingIntent Audit¶
Search the codebase for PendingIntent creation and check:
- Is
FLAG_MUTABLEused? If so, is the base intent fully explicit (component and package set)? - Is the base intent empty (
new Intent())? This is exploitable. - Is the
PendingIntentexposed through notifications, widgets, or IPC? These are reachable by attackers.
ADB-Based Quick Tests¶
Without Drozer, use ADB directly to probe exported components:
adb shell am start -n com.target.app/.ExportedActivity \
--es sensitive_key "test_value"
adb shell am broadcast -a com.target.app.CUSTOM_ACTION \
--es data "injected"
adb shell am startservice -n com.target.app/.ExportedService
If these commands successfully trigger functionality that should be internal-only, the app has an intent hijacking vulnerability.