Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
LeakValue — Exploit for CVE-2022-20452, privilege escalation on Android from installed app to system app (or another app) via LazyValue using Parcel after recycle() | Kitploit
Tools/GitHubGitHub/michalbednarski/leakvalue
Android SecurityPrivilege EscalationVulnerability AnalysisExploitationMobile SecurityBinary Exploitation
GitHubmichalbednarski/leakvalue

LeakValue

Exploit for CVE-2022-20452, privilege escalation on Android from installed app to system app (or another app) via LazyValue using Parcel after recycle()

View Repository
34765234 years agoReviewed by Kitploit

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

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:

  • CVE-2022-20452 (bulletin, patch)
  • CVE-2022-20474 (bulletin, patch)

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

(Also logcat from app execution, exploitation is noisy in logs)

Introduction to Parcel and Parcelable mismatch bugs

Android'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

Enter LazyValue, the end of self-changing Bundles

Download Tool