
Exploit for CVE-2022-20452, privilege escalation on Android from installed app to system app (or another app) via LazyValue using Parcel after recycle()
Android 13 introduces many enhancements in order to harden Parcel serialization mechanism
Here's presentation from Android Security and Privacy team on enhancements made
That is great, definitely eliminates or makes unexploitable many vulnerabilities. Also they describe breaking my previous exploit, allowing apps to load their code into other apps (including system ones)
But now I am back with new exploit that achieves the same, although in different way. It relies on following vulnerabilities that were introduced during aforementioned Parcel hardening:
![Screenshot of application displaying text. Title: LeakValue. Main text: Created 6 ValueLeaker-s. Locking ActivityTaskManagerService. Locked ActivityTaskManagerService. Unlocking ActivityTaskManagerService. Unlocked ActivityTaskManagerService. leakedBinders=[android.os.BinderProxy@f06702e]. Leaked interface: android.app.IApplicationThread. Requesting code execution. Shellcode has been executed in uid=1000 pid=6904 packageName=com.android.settings uid=1000(system) gid=1000(system) groups=1000(system),1007(log),1065(reserved_disk),1077(external_storage),3001(net_bt_admin),3002(net_bt),3003(inet),3007(net_bw_acct),9997(everybody) context=u:r:system_app:s0. At bottom of screen there are two buttons: START and MANUAL TESTING](Screenshot_20220723-081920.png)
(Also logcat from app execution, exploitation is noisy in logs)
Parcel and Parcelable mismatch bugsAndroid's Parcel class is base of communication between processes
Objects can implement Parcelable interface in order to allow writing them to Parcel, for example (copied from AOSP):
public class UsbAccessory implements Parcelable {
public static final Parcelable.Creator<UsbAccessory> CREATOR =
new Parcelable.Creator<UsbAccessory>() {
public UsbAccessory createFromParcel(Parcel in) {
String manufacturer = in.readString();
String model = in.readString();
String description = in.readString();
String version = in.readString();
String uri = in.readString();
IUsbSerialReader serialNumberReader = IUsbSerialReader.Stub.asInterface(
in.readStrongBinder());
return new UsbAccessory(manufacturer, model, description, version, uri,
serialNumberReader);
}
};
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(mManufacturer);
parcel.writeString(mModel);
parcel.writeString(mDescription);
parcel.writeString(mVersion);
parcel.writeString(mUri);
parcel.writeStrongBinder(mSerialNumberReader.asBinder());
}
}
Note that Parcel internally stores position at which write or read is performed, readString() parses data into String as well as advances position. That position can be manually get/set through dataPosition()/setDataPosition(). Implementations of Parcelable interface must ensure that their writeToParcel and createFromParcel write/read same amount of data, otherwise all subsequent reads will get data from wrong offsets
Bundle (key-value map that can be sent across processes) can contain variety of objects that can be written to Parcel through writeValue(). When contents of Bundle are read from Parcel, any Parcelable class available in system there can be read
Bundle defers actual parsing of contents by having length of whole parcelled data written into Parcel and then just copying relevant part of original Parcel to secondary Parcel stored in mParcelledData (this allows for example Activity.onSaveInstanceState() to provide Parcelables which are not available in system_server, whole Bundle is then passed to system_server and back verbatim without parsing contents)
Once however any value in Bundle was accessed, all values inside Bundle were unparcelled and every present key-value pair was parsed. If such map contained Parcelable which had unbalanced writeToParcel and createFromParcel methods and later such Bundle was forwarded to another process, that another process could see different contents of Bundle. This made all such mismatches in classes available in system vulnerabilities as there are places in system where Bundle is inspected to be safe and then forwarded to another process
In this writeup I'm calling such Bundle which presents one contents and then other after being forwarded a self-changing Bundle
Another important thing here is that besides just bytes (Strings, numbers, objects made of above), Parcel can also contain File Descriptors and Binders. Binders are objects on which one can make RPC call, that is one process creates Binder object and overrides onTransact() method. Then Binder is passed to another process, in example code above you can see read/writeStrongBinder() calls used to read and write it to Parcel. In another process, when readStrongBinder() is used a BinderProxy object is created (hidden behind IBinder interface). Then that another process can call transact() on that object and in original object onTransact() will be executed. Usually through, one doesn't manually write transact()/onTransact() but uses AIDL instead
LazyValue, the end of self-changing Bundles