Privilege escalation to system app via LazyValue using Parcel after recycle() [CVE-2022-20452]

We discussed this vulnerability during Episode 179 on 16 January 2023

tl;dr Android Parcels have their own memory pool rather than being free’d all the way back to the general Java memory pool. This custom memory management, combined with a bug resulting in a dangling reference in a Parcel to an older version of the parcel creates a “use-after-free” like situation

Quick background, you’ve got bundles, bundles are just key-value mappings with a limited set of allowed value types. One of the allowed value types is a Parcelable. Parcels have been a source of vulnerabilities in Bundle processing for awhile. “Unbalanced” parcels are where the parcel implementation does not read the same amount of data as it had written, resulting in later values from the Parcel being read from the wrong offset.

LazyValue are attempting to deal with these problems by storing size information with each value in the parcel, allowing it to know how much data should be read, and introducing an extra layer of abstraction around the value. There is also some functionality for string deduplication using a Parcel.ReadWriteHelper. The details of how that works doesn’t matter but it changes the flow a bit. Adding in a step to immediately deserialize the parcel so the helper can be released. It copies the Parcel, and eventually tries to resolve all of the LazyValues inside.

Normally after this there would be no LazyValues, however if an exception is raised (atleast within teh system_server), the LazyValue is just ignored and left in-place. When this happens you have a LazyValue that has been copied into this new Parcel, but that still points to the value in the original Parcel.

Normally this wouldn’t be an issue, but Parcels have their own memory management system apart from the Java garbage collector. So that original Parcel may get recycled into that pool, and reused for another parcel. Creating a situation very similar to a use-after-free.