EvilParcel vulnerabilities analysis

    Introduction


    In mid-April, we published news about the Android.InfectionAds.1 trojan, which exploited several critical vulnerabilities in Android. One of them, CVE-2017-13156 (also known as Janus), allows malware to infect APK files without damaging the digital signature. The other one is CVE-2017-13315. It gives the trojan extended privileges, so that it can install and uninstall applications independently from user. A detailed analysis of Android.InfectionAds.1 is available in our virus library; while we’re here we will touch upon the CVE-2017-13315 vulnerability and see what it does.

    CVE-2017-13315 belongs to the group of vulnerabilities dubbed EvilParcel. They are found in various Android system classes. Errors in these classes make it possible to substitute information during the data exchange between apps and the system. Malware that exploit the EvilParcel vulnerabilities are thus granted higher privileges and become capable of the following:

    • installing and removing applications with any permissions without confirmation from users;
    • infecting software installed on the device and replacing clean originals with infected copies when used together with other vulnerabilities;
    • resetting the lock screen PIN on Android devices.

    As of now, we know about 7 vulnerabilities of this type:
    • CVE-2017-0806 (error in the GateKeeperResponse class), published in October 2017;
    • CVE-2017-13286 (error in the OutputConfiguration class, published in April 2018;
    • CVE-2017-13287 (error in the VerifyCredentialResponse class), published in April 2018;
    • CVE-2017-13288 (error in the PeriodicAdvertizingReport class), published in April 2018;
    • CVE-2017-13289 (error in the ParcelableRttResults class), published in April 2018;
    • CVE-2017-13311 (error in the SparseMappingTable class), published in May 2018;
    • CVE-2017-13315 (error in the DcParamObject class), published in May 2018.

    All of them pose a threat to devices running Android 5.0 — 8.1 with no May 2018 (or later) security update installed.

    Prerequisites for EvilParcel vulnerabilities


    Let's see how EvilParcel vulnerabilities can appear. First of all, we need to look into some features of the Android applications. All Android programs interact with each other, as well as with the operating system, by sending and receiving Intent objects. These objects can contain an arbitrary number of key-value pairs inside a Bundle object.

    When transferring an Intent, a Bundle object is converted (serialized) into a byte array wrapped in Parcel, and then automatically deserialized after reading keys and values from a serialized Bundle.

    In the Bundle, the key is the string, and the value can be almost anything. For instance, it can be a primitive type, a string, or a container with primitive types or strings. It can also be a Parcelable object.

    Thus, the Bundle can contain an object of any type that implements the Parcelable interface. For this, we need to implement the writeToParcel() and createFromParcel() methods to serialize and deserialize the object.

    To illustrate our point, let’s create a simple serialized Bundle. We’ll write a code that puts three key-value pairs in the Bundle and serializes it:


    Figure 1. Structure of a serialized Bundle object

    Note the specific features of Bundle serialization:

    • all key-value pairs are written sequentially;
    • the value type is indicated before each value (13 for the byte array, 1 for the integer, 0 for the string, etc.);
    • variable length data size is indicated before the data (length for the string, number of bytes for the array);
    • all values are 4-byte aligned.


    All keys and values are written in the Bundle sequentially so that when accessing any key or value of a serialized Bundle object, the latter deserializes entirely, also initializing all contained Parcelable objects.

    So, what could be the problem? The problem is that some system classes that implement Parcelable may contain errors in the createFromParcel() and writeToParcel() methods. In these classes, the number of bytes read in createFromParcel() will differ from the number of bytes written in writeToParcel(). If you place an object of this class inside a Bundle, the object boundaries inside the Bundle will change after reserialization. This creates the conditions for exploiting an EvilParcel vulnerability.

    Let’s see an example of a class that contains this error:

    class Demo implements Parcelable {
        byte[] data;
        public Demo() {
          this.data = new byte[0];
        }
        protected Demo(Parcel in) {
          int length = in.readInt();
          data = new byte[length];
          if (length > 0) {
             in.readByteArray(data);
          }
         }
         public static final Creator<Demo> CREATOR = new Creator<Demo>() {
           @Override
           public Demo createFromParcel(Parcel in) {
             return new Demo(in);
           }
         };
         @Override
         public void writeToParcel(Parcel parcel, int i) {
           parcel.writeInt(data.length);
           parcel.writeByteArray(data);
         }
    }
    

    If the data array size is 0, then when creating an object, one int (4 bytes) will be read in createFromParcel() and two int (8 bytes) will be written in writeToParcel(). The first int will be written by explicitly calling writeInt. The second int will be written when calling writeByteArray(), since the array length is always written before the array in Parcel (see Figure 1).

    Situations where the data array size equals 0 are pretty rare. But even when this happens, the program keeps operating, if you only transmit one serialized object at a time (in our example, the Demo object). Therefore, such errors tend to go unnoticed.

    Now we’ll try to place a Demo object with zero array length in the Bundle:


    Figure 2. The result of adding a zero-length Demo object to the Bundle

    We serialize the object:


    Figure 3. The Bundle object after serialization

    Now let’s try to deserialize it:


    Figure 4. The Bundle object after deserialization

    What do we get? Let’s have a look at the Parcel fragment:


    Figure 5. Parcel structure after Bundle deserialization

    In Figures 4 and 5, we see that instead of two int, one int was read in the createFromParcel method during deserialization. Therefore, all subsequent values from the Bundle were read incorrectly. The 0x0 value at 0x60 was read as the length of the next key. The 0x1 value at 0x64 was read as a key. The 0x31 value at 0x68 was read as a value type. Parcel has no values with type 0x31, therefore readFromParcel() painstakingly reports an exception.

    How can this be used in real life and become a vulnerability? Let’s see! The above error in the Parcelable system classes enables the creation of Bundles that may differ during the first and repeated deserializations. To demonstrate this, we’ll modify the previous example:

    Parcel data = Parcel.obtain();
    data.writeInt(3); // 3 entries
    data.writeString("vuln_class");
    data.writeInt(4); // value is Parcelable
    data.writeString("com.drweb.testbundlemismatch.Demo");
    data.writeInt(0); // data.length
    data.writeInt(1); // key length -> key value
    data.writeInt(6); // key value -> value is long
    data.writeInt(0xD); // value is bytearray -> low(long)
    data.writeInt(-1); // bytearray length dummy -> high(long)
    int startPos = data.dataPosition();
    data.writeString("hidden"); // bytearray data -> hidden key
    data.writeInt(0); // value is string
    data.writeString("Hi there"); // hidden value
    int endPos = data.dataPosition();
    int triggerLen = endPos - startPos;
    data.setDataPosition(startPos - 4);
    data.writeInt(triggerLen); // overwrite dummy value with the real value
    data.setDataPosition(endPos);
    data.writeString("A padding");
    data.writeInt(0); // value is string
    data.writeString("to match pair count");
    int length = data.dataSize();
    Parcel bndl = Parcel.obtain();
    bndl.writeInt(length);
    bndl.writeInt(0x4C444E42); // bundle magic
    bndl.appendFrom(data, 0, length);
    bndl.setDataPosition(0);

    This code creates a serialized Bundle that contains a vulnerable class. Now let’s see what we get after executing this code:


    Figure 6. Creating a Bundle with a vulnerable class

    After the first deserialization, this Bundle will contain the following keys:


    Figure 7. After deserialization of a Bundle with a vulnerable class

    Now we will serialize the Bundle again, then deserialize it again, and look at the list of keys:


    Figure 8. Result of reserializing and deserializing of a Bundle with a vulnerable class

    What do we see? The Bundle now contains the Hidden key (with the string value «Hi there!»), which was not there before. Let us look into the Parcel fragment of this Bundle to see why this happened:


    Figure 9. Parcel structure of a Bundle object with a vulnerable class after two serialization and deserialization cycles

    This is where we can see the whole point of EvilParcel vulnerabilities. We can specifically create a Bundle that will contain a vulnerable class. Changing the boundaries of this class will allow the placement of any object in this Bundle; for example, an Intent, which will only appear in the Bundle after the second deserialization. This helps to hide an Intent from the OS security mechanisms.

    Exploiting EvilParcel


    Android.InfectionAds.1 exploited CVE-2017-13315 to install and remove software independently from device owners. But how?

    In 2013, the error 7699048, also known as Launch AnyWhere, was discovered. It allowed third-party applications to start arbitrary activities on behalf of a more privileged system user. See the diagram below for the action mechanism:


    Figure 10. Operation of the error 7699048

    An exploiting application can use this vulnerability to implement the AccountAuthenticator service, designed to add new accounts to the operating system. The error 7699048 helps the exploit launch activities to install, remove, replace applications, as well as reset the PIN or Pattern Lock and cause much more trouble.

    Google Inc. has eliminated this breach by prohibiting the launch of arbitrary activity from AccountManager. Now, AccountManager only allows the launch of activities originating from the same application. For this purpose, it checks and matches the digital signature of software that initiated the activity with the signature of the application where the activity is located. It looks like this:

    if (result != null
       && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
       /*
        * The Authenticator API allows third party authenticators to
        * supply arbitrary intents to other apps that they can run,
        * this can be very bad when those apps are in the system like
        * the System Settings.
        */
       int authenticatorUid = Binder.getCallingUid();
       long bid = Binder.clearCallingIdentity();
       try {
         PackageManager pm = mContext.getPackageManager();
         ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
         int targetUid = resolveInfo.activityInfo.applicationInfo.uid;
         if (PackageManager.SIGNATURE_MATCH !=
               pm.checkSignatures(authenticatorUid, targetUid)) {
              throw new SecurityException(
                  "Activity to be started with KEY_INTENT must " +
                  "share Authenticator's signatures");
         }
       } finally {
         Binder.restoreCallingIdentity(bid);
       }
    }
    

    It would seem that the problem has been solved, but it’s not as easy as all that. It turned out that the well-known vulnerability, EvilParcel CVE-2017-13315, provides a workaround! As we already know, after fixing Launch AnyWhere, the system verifies the digital signature of the application. If it is successfully verified, the Bundle is transferred to IAccountManagerResponse.onResult(). At the same time, onResult() is called via the IPC mechanism, so the Bundle is serialized again. While implementing onResult(), the following happens:

    /** Handles the responses from the AccountManager */
    private class Response extends IAccountManagerResponse.Stub {
        public void onResult(Bundle bundle) {
          Intent intent = bundle.getParcelable(KEY_INTENT);
          if (intent != null && mActivity != null) {
            // since the user provided an Activity we will silently start intents
            // that we see
            mActivity.startActivity(intent);
            // leave the Future running to wait for the real response to this request
          }
          //<.....>
         }
         //<.....>
    }
    

    Then, the Bundle extracts the intent key, and the activity is launched without any verification.
    Thus, to launch an arbitrary activity with system privileges, you only need to create a Bundle with the Intent field hidden upon the first deserialization and appearing during the repeated deserialization.
    As we already know, EvilParcel vulnerabilities can actually perform this task.

    At the moment, all known vulnerabilities of this type have been fixed within the vulnerable Parcelable classes. However, new vulnerable classes may appear in the future. Bundle implementation and the mechanism for adding new accounts are still the same as before. They still allow us to create this exact exploit when detecting old or new vulnerable Parcelable classes. Moreover, these classes are still implemented manually, and the programmer has to make sure that the length of the serialized Parcelable object stays the same, which is a human factor with all that it implies. However, we hope there will be as few such errors as possible, and the EvilParcel vulnerabilities will not be a threat to Android users.

    You can check your mobile device for EvilParcel vulnerabilities using our Dr.Web Security Space for Android. The built-in Security Auditor will report the detected issues and recommend ways to eliminate them.
    Доктор Веб
    115.77
    Company
    Share post

    Comments 0

    Only users with full accounts can post comments. Log in, please.