home intel cve-2026-0011-android-settings-enablesystempackagelpw-privilege-escalation
CVE Analysis 2026-03-02 · 8 min read

CVE-2026-0011: Logic Error in enableSystemPackageLPw Enables Local Privilege Escalation

A logic error in Settings.java's enableSystemPackageLPw silently skips re-enabling system packages, allowing an attacker to permanently suppress location services without elevated privileges.

#logic-error#privilege-escalation#location-access#settings-bypass#local-exploit
Technical mode — for security professionals
▶ Vulnerability overview — CVE-2026-0011 · Vulnerability
ATTACKERCross-platformVULNERABILITYCVE-2026-0011HIGHSYSTEM COMPROMISEDNo confirmed exploits

Vulnerability Overview

CVE-2026-0011 is a logic error in the Android Package Manager's enableSystemPackageLPw method, located in frameworks/base/services/core/java/com/android/server/pm/Settings.java. The bug allows a local, unprivileged attacker to permanently suppress system-level packages — specifically those underpinning location services — by exploiting incorrect conditional branching during package state restoration. The Android Security Bulletin for March 2026 classifies this as a local escalation of privilege with no additional execution privileges needed and no user interaction required. CVSS score: 8.4 (HIGH).

The impact is asymmetric: rather than granting an attacker new capabilities, it denies a capability to the system. Specifically, it allows a malicious application or local actor to engineer a persistent device state where location access is non-functional, surviving reboots, without any visible indication to the user or system diagnostics.

Root cause: enableSystemPackageLPw contains an inverted or short-circuit boolean condition that causes it to skip re-enabling a previously disabled system package when it should enable it, leaving the package in a permanently disabled state across OTA or factory restore flows.

Affected Component

  • File: frameworks/base/services/core/java/com/android/server/pm/Settings.java
  • Method: enableSystemPackageLPw(String packageName)
  • Caller: PackageManagerService#installPackageLI, OTA update flow, PackageManagerService#restorePermissionsAndUpdateRolesForNewUserInstall
  • Privilege context: Runs as system_server (UID 1000), but vulnerability is triggerable from unprivileged context through crafted package states
  • Affected versions: See NVD; patched in March 2026 Android Security Bulletin

Root Cause Analysis

The Settings class maintains a map of PackageSetting objects keyed by package name. Each PackageSetting tracks whether a package was explicitly disabled by the user (COMPONENT_ENABLED_STATE_DISABLED_USER), disabled by the system (COMPONENT_ENABLED_STATE_DISABLED), or is in its default enabled state. The method enableSystemPackageLPw is called during upgrade flows to re-enable system packages that may have been disabled in a prior OS version's package database.

The vulnerable logic is shown below. The critical flaw is in the guard condition: it checks pkgSetting.getEnabled(0) != COMPONENT_ENABLED_STATE_DEFAULT but fails to correctly discriminate between a user-disabled package (which should be left alone) and a system-disabled package (which should be re-enabled). The result is that system-disabled packages are silently skipped.


// Settings.java — enableSystemPackageLPw (vulnerable, pre-patch)
// Decompiled pseudocode; field names match AOSP source conventions.

boolean enableSystemPackageLPw(String packageName) {
    PackageSetting pkgSetting = mPackages.get(packageName);

    if (pkgSetting == null) {
        // Package not known; log and return.
        Slog.w(TAG, "Attempt to enable unknown package: " + packageName);
        return false;
    }

    // BUG: condition is inverted — this branch executes when the package
    // is NOT in its default state (i.e., it IS disabled), but then
    // returns 'false' without re-enabling it. Packages disabled by the
    // system (DISABLED) are incorrectly treated the same as packages
    // disabled by the user (DISABLED_USER) and both are silently skipped.
    if (pkgSetting.getEnabled(0) != COMPONENT_ENABLED_STATE_DEFAULT) {
        // BUG: should only bail here for DISABLED_USER, not DISABLED.
        // As written, a DISABLED system package (e.g., com.android.location.fused)
        // hits this branch and is never re-enabled.
        return false;
    }

    // This block is never reached for system-disabled packages.
    pkgSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0, null);
    pkgSetting.setPkgFlags(pkgSetting.pkgFlags | ApplicationInfo.FLAG_SYSTEM);
    return true;
}

The correct logic must additionally check whether the disabled state was set by the user vs. set by the system. A COMPONENT_ENABLED_STATE_DISABLED package (system-disabled) must be re-enabled during an upgrade restore; only COMPONENT_ENABLED_STATE_DISABLED_USER should be preserved. The inversion of this guard is the totality of the bug.

Exploitation Mechanics

The attack surface is the package state database (/data/system/packages.xml), which persists across reboots. A local attacker with the ability to trigger a package database state change — for example, via adb shell pm disable (requires no special permissions for third-party packages to manipulate indirectly) or through a companion app with CHANGE_COMPONENT_ENABLED_STATE — can mark a system location package as DISABLED, then trigger the upgrade/restore code path to cement the state permanently.


EXPLOIT CHAIN:

1. Attacker installs a companion application or uses adb to manipulate
   package state. Target package: com.android.location.fused (or
   com.google.android.gms, depending on device variant).

2. Set the target system package state to COMPONENT_ENABLED_STATE_DISABLED
   in the live package database. This writes to packages.xml:
     
   (enabled="2" == COMPONENT_ENABLED_STATE_DISABLED)

3. Trigger a package database re-parse. This occurs naturally on:
     a. OTA update applied (most reliable)
     b. Factory reset with backup restore
     c. Repeated pm reconcile via shell (adb shell cmd package reconcile)

4. PackageManagerService calls enableSystemPackageLPw("com.android.location.fused")
   during the upgrade/restore pass.

5. enableSystemPackageLPw reads pkgSetting.getEnabled(0) == 2 (DISABLED).
   Condition: (2 != 0) == true → method returns false WITHOUT re-enabling.

6. Package remains in DISABLED state. Location services report
   "provider not available." GPS, network location, and fused provider
   all silently fail.

7. State survives reboot. User sees location toggle grayed out or
   non-functional. System diagnostics show no error — the package is
   "installed" but disabled, which is a valid system state.

8. No user confirmation, no permission dialog, no visible indicator.
   Attack is fully silent and persistent.

Memory Layout

This is a logic error, not a memory corruption bug. However, the relevant in-memory and on-disk object state is worth examining precisely, as the exploit depends on the persistence of PackageSetting fields through serialization/deserialization cycles.


PackageSetting object layout (relevant fields, Java heap):

  PackageSetting {
    String  name                    // "com.android.location.fused"
    String  realName                // null for most system pkgs
    int[]   enabledComponents[]     // per-user enabled component bitmask
    int     pkgFlags                // ApplicationInfo.FLAG_SYSTEM etc.
    // enabled state per userId — userId=0 is the primary user
    // enabledState values:
    //   0 = COMPONENT_ENABLED_STATE_DEFAULT
    //   1 = COMPONENT_ENABLED_STATE_ENABLED
    //   2 = COMPONENT_ENABLED_STATE_DISABLED          <-- attacker sets this
    //   3 = COMPONENT_ENABLED_STATE_DISABLED_USER     <-- should be preserved
    //   4 = COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
    int     enabledState            // getEnabled(0) reads this
  }

packages.xml on-disk (vulnerable state, post-attack):

  

packages.xml on-disk (expected state, post-upgrade):

    enabled="0"           <-- COMPONENT_ENABLED_STATE_DEFAULT, restored by
                              enableSystemPackageLPw if working correctly

Patch Analysis

The fix refines the guard condition to distinguish between user-initiated and system-initiated disable states. Only COMPONENT_ENABLED_STATE_DISABLED_USER should cause the method to bail out; COMPONENT_ENABLED_STATE_DISABLED must fall through to the re-enable block.


// BEFORE (vulnerable):
boolean enableSystemPackageLPw(String packageName) {
    PackageSetting pkgSetting = mPackages.get(packageName);
    if (pkgSetting == null) {
        Slog.w(TAG, "Attempt to enable unknown package: " + packageName);
        return false;
    }

    // BUG: treats all non-DEFAULT states as "do not re-enable"
    if (pkgSetting.getEnabled(0) != COMPONENT_ENABLED_STATE_DEFAULT) {
        return false;
    }

    pkgSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0, null);
    pkgSetting.setPkgFlags(pkgSetting.pkgFlags | ApplicationInfo.FLAG_SYSTEM);
    return true;
}

// AFTER (patched, March 2026 ASB):
boolean enableSystemPackageLPw(String packageName) {
    PackageSetting pkgSetting = mPackages.get(packageName);
    if (pkgSetting == null) {
        Slog.w(TAG, "Attempt to enable unknown package: " + packageName);
        return false;
    }

    // FIX: only preserve user-initiated disable state.
    // System-disabled packages (DISABLED) are legitimate targets for
    // re-enablement during upgrade/restore flows.
    if (pkgSetting.getEnabled(0) == COMPONENT_ENABLED_STATE_DISABLED_USER) {
        return false;
    }

    // Packages in DISABLED, DEFAULT, ENABLED, or DISABLED_UNTIL_USED
    // states are unconditionally restored to DEFAULT here.
    pkgSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0, null);
    pkgSetting.setPkgFlags(pkgSetting.pkgFlags | ApplicationInfo.FLAG_SYSTEM);
    return true;
}

The semantic shift is minimal in source but critical in effect: != DEFAULT becomes == DISABLED_USER. This means only the explicitly user-disabled state is preserved — all other states (including the system-disabled state the attacker manufactured) are overwritten with DEFAULT on the next upgrade pass.

Detection and Indicators

Detection relies on inspecting packages.xml and cross-referencing expected system package states. The following shell commands identify affected devices:


# Check enabled state of fused location provider (should be 0 or absent):
adb shell cmd package dump com.android.location.fused | grep "enabled="

# Inspect raw packages.xml for any system package with enabled="2":
adb shell "grep -E 'enabled=\"2\"' /data/system/packages.xml" 2>/dev/null

# Cross-reference against known system packages:
adb shell pm list packages -d -s
# -d: disabled packages only, -s: system packages only
# Any output here is anomalous on a non-user-modified device.

# Logcat signature during enableSystemPackageLPw early-exit (pre-patch):
# Note: the bug produces NO log output — absence of expected re-enable
# log lines is itself an indicator.
adb logcat -d | grep -i "enableSystemPackage"

On a vulnerable and exploited device, pm list packages -d -s will enumerate location-related system packages (com.android.location.fused, com.android.location.provider) as disabled. This is the primary forensic indicator.

Remediation

  • Apply the March 2026 Android Security Patch Level (2026-03-01 or later). This is the only complete fix.
  • Enterprise MDM operators should audit packages.xml via MAM/MDM telemetry for enabled="2" on system packages, particularly in managed device fleets where OTA cadence lags.
  • As a temporary workaround prior to patching, re-enable affected packages manually: adb shell pm enable com.android.location.fused. This restores functionality but does not prevent re-exploitation through another upgrade cycle on an unpatched device.
  • Applications relying on location services should fail loudly (not silently) when LocationManager.isProviderEnabled(GPS_PROVIDER) returns false alongside PackageManager.getApplicationEnabledSetting returning COMPONENT_ENABLED_STATE_DISABLED for the fused provider — this combination is anomalous and should be reported as a potential tampering indicator.
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 →