EvilParcel Vulnerability Analysis

    Introduction


    In mid-April, we published news about the Android.InfectionAds.1 Trojan , which exploited several critical vulnerabilities in the Android OS. One of them - CVE-2017-13156 (also known as Janus ) - allows a malicious program to infect APK files without damaging their digital signature.

    The other is CVE-2017-13315. It gives the Trojan advanced privileges, and it can independently install and uninstall applications. A detailed analysis of Android.InfectionAds.1 is available in our virus library, it can be found here . We will dwell on the vulnerability CVE-2017-13315 in more detail and see what it is like.

    CVE-2017-13315 belongs to the group of vulnerabilities that received the general name EvilParcel. They are found in various system classes of the Android OS. Due to errors in the latter when exchanging data between applications and the system, it becomes possible to replace this data. Malicious programs that exploit EvilParcel vulnerabilities receive higher privileges and can do the following with their help:

    • install and uninstall applications with any permissions without user confirmation;
    • when used in conjunction with other vulnerabilities, infect installed programs on the device and replace “clean” originals with infected copies;
    • Reset screen lock code for Android device
    • Reset Android device lock screen PIN.

    There are currently 7 known vulnerabilities of this type:

    • CVE-2017-0806 (error in the GateKeeperResponse class), published in October 2017;
    • CVE-2017-13286 (error in the class OutputConfiguration, 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 (bug in the ParcelableRttResults class), published in April 2018;
    • CVE-2017-13311 (bug in the SparseMappingTable class), published in May 2018;
    • CVE-2017-13315 (error in the DcParamObject class), published in May 2018.

    All of them threaten devices running Android OS versions 5.0 - 8.1 that do not have the May 2018 and later security updates installed.

    Prerequisites for EvilParcel Vulnerabilities


    Let's see how EvilParcel vulnerabilities arise. First of all, we’ll look at some of the features of Android applications. In Android OS, all programs interact with each other, as well as with the operating system itself, by sending and receiving objects of type Intent. These objects can contain an arbitrary number of key-value pairs inside an object of type Bundle.

    When transmitting Intent, the Bundle object is converted (serialized) to a byte array wrapped in Parcel, and when reading keys and values ​​from a serialized Bundle, it is automatically deserialized.

    In the Bundle, the string is the key, and the value can be almost anything. For example, a primitive type, string, or container containing primitive types or strings. In addition, it can be an object of type Parcelable.

    Thus, in the Bundle, you can place an object of any type that implements the Parcelable interface. To do this, you will need to implement the writeToParcel () and createFromParcel () methods for serializing and deserializing the object.

    As a good example, let's create a simple serialized Bundle. We will write a code that will place three key-value pairs in the Bundle and serialize it:

    Bundle demo = new Bundle ();
    demo.putString ("String", "Hello, World!");
    demo.putInt ("Integer", 42);
    demo.putByteArray ("ByteArray", new byte [] {1, 2, 3, 4, 5, 6, 7, 8});
    Parcel parcel = Parcel.obtain ();
    parcel.writeBundle (demo);

    After executing this code, we get a bundle of the following form:



    Figure 1.The structure of a serialized Bundle object.

    Let's pay attention to the following features of Bundle serialization:

    • all key-value pairs are written one after another;
    • before each value its type is indicated (13 for a byte array, 1 for an Integer, 0 for a string, and so on);
    • before data of variable length, their size is indicated (length for the string, number of bytes for the array);
    • all values ​​are written with an alignment of 4 bytes.

    Due to the fact that all keys and values ​​in the Bundle are written sequentially, when accessing any key or the value of a serialized Bundle object, the latter is deserialized completely, including initializing all Parcelable objects contained in it.

    It would seem, what could be the problem? And it is that in some system classes that implement Parcelable, the createFromParcel () and writeToParcel () methods may encounter errors. In these classes, the number of bytes read in the createFromParcel () method will be different from the number of bytes written in the writeToParcel () method. If you place an object of such a class inside the Bundle, the boundaries of the object inside the Bundle will change after re-serialization. And this is where the conditions for exploiting the EvilParcel vulnerability are created.

    Here is an example of a class with a similar 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 CREATOR = new Creator() {
           @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 size of the data array is 0, then when creating an object in createFromParcel () one int (4 bytes) will be read, and two int (8 bytes) will be written in writeToParcel (). The first int will be written in an explicit call to writeInt. The second int will be written when writeByteArray () is called, because its length is always written to the array before Parcel (see Figure 1).

    Situations when the size of the data array is 0 are rare. But even when this happens, the program still continues to work if only one object is transmitted in serialized form at a time (in our example, the Demo object). Therefore, such errors, as a rule, go unnoticed.

    Now let's try to place a Demo object with a 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.

    Let's try to deserialize it:


    Figure 4. After deserializing the Bundle object.

    What is the result? Consider a Parcel fragment:


    Figure 5. Parcel structure after deserialization of the Bundle.

    From Figures 4 and 5, we see that during deserialization, one int was read in the createFromParcel method instead of two previously written ones. Therefore, all subsequent values ​​from the Bundle were not read correctly. The value 0x0 at address 0x60 was read as the length of the next key. And the value 0x1 at address 0x64 was read as a key. In this case, the value 0x31 at address 0x68 was read as the type of value. There are no values ​​in Parcel whose type is 0x31, so readFromParcel () faithfully reported an error (exception).

    How can this be used in practice and become a vulnerability? Let's get a look! The error described above in the Parcelable system classes allows you to construct Bundle, which may differ during the first and repeated deserializations. To demonstrate this, 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. Let's look at the result of 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. Result of deserializing a Bundle with a vulnerable class.

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


    Figure 8. Result of repeated serialization and deserialization of the Bundle with the vulnerable class.

    What do we see? The hidden key (with the string value “Hi there!”) Appeared in the Bundle, which was not there before. Consider the Parcel fragment of this Bundle to understand why this happened:


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

    Here the essence of EvilParcel vulnerabilities becomes more clear. It is possible to create a specially formed Bundle that will contain a vulnerable class. Changing the boundaries of this class will allow you to place any object in this Bundle - for example, Intent, which appears in the Bundle only after the second deserialization. This will make it possible to hide Intent from the protection mechanisms of the operating system.

    Operation EvilParcel


    Android.InfectionAds.1 using CVE-2017-13315 installed and uninstalled programs on its own without the intervention of the owner of the infected device. But how is this going?

    In 2013, error 7699048 was also discovered , also known as Launch AnyWhere. It allowed a third-party application to run arbitrary activities on behalf of the more privileged user system. The diagram below shows the mechanism of its action:


    Figure 10. Scheme of error 7699048.

    With this vulnerability, an exploit application could implement the AccountAuthenticator service, which is designed to add new accounts to the operating system. Thanks to bug 7699048, the exploit is able to run activity to install, uninstall, replace applications, reset the PIN or Pattern Lock, and do other unpleasant things.

    Google has fixed this gap by prohibiting the launch of arbitrary activity from the AccountManager. Now the AccountManager only allows the launch of activities coming from the same application. To do this, it checks and compares the digital signature of the program that initiated the start of the activity with the signature of the application in which the launched 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 not everything here is so smooth. It turned out that this fix can be circumvented using the well-known vulnerability EvilParcel CVE-2017-13315! As we already know, after fixing Launch AnyWhere, the system checks the digital signature of the application. If this check succeeds, the Bundle is passed to IAccountManagerResponse.onResult (). At the same time, onResult () is called through the IPC mechanism, so the Bundle is serialized again. In the onResult () implementation, 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
          }
          //<.....>
         }
         //<.....>
    }
    

    Next, the Bundle is extracted the intent key and the activity is launched without checks. As a result, to start an arbitrary activity with system rights, it is enough to construct the Bundle in such a way that the intent field is hidden during the first deserialization, and appears when the deserialization is repeated. And, as we have already seen, it is precisely this task that EvilParcel vulnerabilities fulfill.

    At the moment, all known vulnerabilities of this type are fixed by fixes in the vulnerable Parcelable classes themselves. However, the reappearance of vulnerable classes in the future cannot be ruled out. The implementation of the Bundle and the mechanism for adding new accounts are still the same as before. They still allow you to create exactly the same exploit when you discover (or new) vulnerable Parcelable classes. Moreover, the implementation of these classes is still done manually, and the programmer must keep an eye on the constant length of the serialized Parcelable object. And this is a human factor with all the consequences. However, we hope that such errors will be as few as possible, and the vulnerabilities of EvilParcel will not bother users of Android devices.

    You can check your mobile device for EvilParcel vulnerabilities using our Dr.Web Security Space antivirus . The built-in “Security Auditor” will report on the identified problems and give recommendations for resolving them.

    Also popular now: