CVE-2024-49744 AI-generated analysis
platform/frameworks/base, services/core AccountManagerService (account authenticator KEY_INTENT validation)
Hardens AccountManagerService.checkKeyIntent so that an authenticator-supplied KEY_INTENT is pinned to the activity component the system resolved and validated, and extends the Intent-subclass check in checkKeyIntentParceledCorrectly to the re-parceled copy.
Root cause
In services/core/java/com/android/server/accounts/AccountManagerService.java, checkKeyIntent resolves the Intent found under AccountManager.KEY_INTENT with pm.resolveActivityAsUser and verifies that the resolved activity is an exported system activity or shares signature capability (CertCapabilities.AUTH) with the authenticator. Before the fix it returned true without modifying the Intent, so the Intent left in the result Bundle was not bound to the activity that had been checked. In addition, checkKeyIntentParceledCorrectly only rejected the case where the original Intent object was a subclass of Intent (intent.getClass() != Intent.class) and did not apply the same check to simulateIntent, the copy obtained by writing and re-reading the Bundle through a Parcel.
How the fix works
- AccountManagerService.java:checkKeyIntent: after the isExportedSystemActivity / hasSignatureCapability check passes, the fix calls intent.setComponent(targetActivityInfo.getComponentName()) and writes the modified Intent back with bundle.putParcelable(AccountManager.KEY_INTENT, intent), so the Intent in the result Bundle is explicit and targets the validated activity.
- AccountManagerService.java:checkKeyIntentParceledCorrectly: the early 'intent != null && intent.getClass() != Intent.class' rejection is removed; after the null handling (intent == null returns simulateIntent == null) a new check returns false if either intent.getClass() != Intent.class or simulateIntent.getClass() != Intent.class, before the existing intent.filterEquals(simulateIntent) comparison.
Completeness
- Covers the KEY_INTENT path in checkKeyIntent: the component is pinned only after the target passes the isExportedSystemActivity / hasSignatureCapability check, and the pinned Intent is written back into the result Bundle.
- The existing early return `if (intent == null) return true;` and the ClipData / content-scheme handling in checkKeyIntent are unchanged.
- In checkKeyIntentParceledCorrectly, the new class check dereferences simulateIntent without a null check when intent is non-null.
- Only the KEY_INTENT key is validated by these methods; the material shows no other Bundle keys being covered or any other call site changed.
Behavior changes
- Clients receiving KEY_INTENT from AccountManager now get an explicit Intent whose component is the system-resolved, validated activity, even if the authenticator supplied an implicit Intent.
- A result Bundle whose re-parceled KEY_INTENT deserializes to an Intent subclass is now rejected (checkKeyIntentParceledCorrectly returns false), in addition to the original object being a subclass.
Per major
- Android 14: same fix: cherry-pick 985bdc676ac5 in android-security-14.0.0_r16 (Change-Id Ied7961c73299681aa5b523cf3f00fd905893116f); at android-security-14.0.0_r29 the setComponent/putParcelable lines are present in checkKeyIntent (7/8 patched lines present).
- Android 15: same fix: commit 5018e2273cb3 in android-security-15.0.0_r4 with an identical diff at shifted line numbers (5067/5088 vs 4978/4999); 7/8 patched lines present at android-security-15.0.0_r17.
- Android 16: not affected per the bulletin (not listed); the patched region (7/8 lines) is present at android-security-16.0.0_r8.
- Android 17: not affected per the bulletin (not listed); the patched region (7/8 lines) is present at android-security-17.0.0_r1.
Unknowns
- The concrete scenario by which an unpinned KEY_INTENT led to privilege escalation, including how the client later uses the Intent (the commit message says only 'Explicitly set component after target activity check' and 'Update Intent subclass check').
- Which callers invoke checkKeyIntent and checkKeyIntentParceledCorrectly and in what order (the surrounding Session code is not included).
- Whether the Android 12/12L/13 branches received the same or an adapted patch (no diff provided for them).
- Whether the case of a non-null intent with a null simulateIntent is reachable, and how the old filterEquals path behaved in that case versus the new unguarded simulateIntent.getClass() call.