OWASP category: MASVS-PLATFORM: Platform Interaction
Overview
An intent redirection occurs when an attacker can partly or fully control the contents of an intent used to launch a new component in the context of a vulnerable app.
The intent used to launch the new component can be supplied in several ways, most commonly either as a serialized intent in an extras
field, or marshaled to a string and parsed. Partial control of parameters can also lead to the same result.
Impact
The impact can vary. An attacker might execute internal functionality in the vulnerable app, or it might access private components like unexported ContentProvider objects.
Mitigations
General
In general, don’t expose functionality related to redirecting nested intents. In cases where it’s unavoidable, apply the following mitigation methods:
- Properly sanitize the bundled information. It’s important to remember to check or clear flags (
GRANT_URI_PERMISSIONS
), and to check where the intent is being redirected. IntentSanitizer can help with this process. - Use
PendingIntent
objects. This prevents your component from being exported and makes the target action intent immutable.
Apps can check where an intent is being redirected using methods such as ResolveActivity
:
Kotlin
val intent = getIntent()
// Get the component name of the nested intent.
val forward = intent.getParcelableExtra<Parcelable>("key") as Intent
val name: ComponentName = forward.resolveActivity(packageManager)
// Check that the package name and class name contain the expected values.
if (name.packagename == "safe_package" && name.className == "safe_class") {
// Redirect the nested intent.
startActivity(forward)
}
Java
Intent intent = getIntent()
// Get the component name of the nested intent.
Intent forward = (Intent) intent.getParcelableExtra("key");
ComponentName name = forward.resolveActivity(getPackageManager());
// Check that the package name and class name contain the expected values.
if (name.getPackageName().equals("safe_package") &&
name.getClassName().equals("safe_class")) {
// Redirect the nested intent.
startActivity(forward);
}
Apps can use IntentSanitizer using logic similar to the following:
Kotlin
val intent = IntentSanitizer.Builder()
.allowComponent("com.example.ActivityA")
.allowData("com.example")
.allowType("text/plain")
.build()
.sanitizeByThrowing(intent)
Java
Intent intent = new IntentSanitizer.Builder()
.allowComponent("com.example.ActivityA")
.allowData("com.example")
.allowType("text/plain")
.build()
.sanitizeByThrowing(intent);
Common mistakes
- Checking if
getCallingActivity()
returns a non-null value. Malicious apps can supply a null value for this function. - Assuming that
checkCallingPermission()
works in all contexts, or that the method throws an exception when it is actually returning an integer.
Debugging features
For apps that target Android 12 (API level 31) or higher, you can enable a debugging feature that, in some cases, helps you detect whether your app is performing an unsafe launch of an intent.
If your app performs both of the following actions, the system detects an unsafe intent launch, and a StrictMode
violation occurs:
- Your app unparcels a nested intent from the extras of a delivered intent.
- Your app immediately starts an app component using that nested intent, such as passing the intent into
startActivity()
,startService()
, orbindService()
.
Resources
Recommended for you
- Note: link text is displayed when JavaScript is off
- Pending intents