How an unvetted nested PendingIntent in Android's ActivityManagerService allowed unprivileged apps to launch internal non-exported activities.
Imagine an armored courier service that only delivers packages for the president. A trickster hands the courier a package labeled 'Ordinary Letters', but inside the box is a sealed command order that says 'Evacuate the city'. Because the courier blindly opens the inner box and hands it directly to the military, the fake command is executed as if the president ordered it.
An untrusted app creates a nested Intent pointing to an internal privileged setting activity.
The app wraps the nested Intent inside an ordinary notification or account manager request.
The privileged system_server extracts the inner Intent and invokes startActivity().
Because the sender is system_server (UID 1000), Android allows launching the non-exported activity.
सरल और समझने योग्य उच्च स्तरीय प्रोग्रामिंग कोड में प्रस्तुत (बिना किसी बाइनरी या असेंबली के)।
// VULNERABLE: services/core/java/com/android/server/am/ActivityManagerService.java
public void startServiceWithIntent(Intent wrapperIntent) {
// ROOT CAUSE:
// Extracts untrusted nested Intent without validating target component or flags!
Intent nestedIntent = wrapperIntent.getParcelableExtra("extra_target_intent");
// Dispatches nested intent using SYSTEM_SERVER credentials!
mContext.startActivity(nestedIntent);
}
// SECURE: ActivityManagerService.java patch
public void startServiceWithIntent(Intent wrapperIntent) {
Intent nestedIntent = wrapperIntent.getParcelableExtra("extra_target_intent");
if (nestedIntent == null) return;
// 1. Resolve target component to verify export status
ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivity(
nestedIntent, PackageManager.MATCH_DEFAULT_ONLY);
// 2. Reject non-exported components requested by external apps
if (resolveInfo != null && !resolveInfo.activityInfo.exported) {
throw new SecurityException("Permission Denial: target activity is not exported");
}
// 3. Clear dangerous grant flags before dispatch
nestedIntent.removeFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
mContext.startActivity(nestedIntent);
}