home intel cve-2026-0020-android-permission-group-bypass
CVE Analysis 2026-03-02 · 9 min read

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.

#permissions-bypass#privilege-escalation#consent-dialog-bypass#local-attack#android-vulnerability
Technical mode — for security professionals
▶ Vulnerability overview — CVE-2026-0020 · Vulnerability
ATTACKERCross-platformVULNERABILITYCVE-2026-0020HIGHSYSTEM COMPROMISEDNo confirmed exploits

Vulnerability Overview

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 Component

  • File: frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
  • Class: ParsedPermissionUtils
  • Method: parsePermissionGroup()
  • Subsystem: Android Package Manager / Permission Framework
  • 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.
CB
CypherByte Research
Mobile security intelligence · cypherbyte.io
// RELATED RESEARCH
// WEEKLY INTEL DIGEST

Get articles like this every Friday — mobile CVEs, threat research, and security intelligence.

Subscribe Free →