Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2018 The AndroCid Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.security.cts;
     18 
     19 import android.test.AndroidTestCase;
     20 
     21 import android.app.Activity;
     22 import android.os.BaseBundle;
     23 import android.os.Bundle;
     24 import android.os.Parcel;
     25 import android.annotation.SuppressLint;
     26 
     27 import java.io.InputStream;
     28 import java.lang.reflect.Field;
     29 import java.util.Random;
     30 
     31 import android.security.cts.R;
     32 import android.platform.test.annotations.SecurityTest;
     33 
     34 public class AmbiguousBundlesTest extends AndroidTestCase {
     35 
     36     @SecurityTest
     37     public void test_android_CVE_2017_13287() throws Exception {
     38         Bundle bundle;
     39         {
     40             Bundle verifyMe = new Bundle();
     41             verifyMe.putString("cmd", "something_safe");
     42             Bundle useMe = new Bundle();
     43             useMe.putString("cmd", "replaced_thing");
     44             Ambiguator a = new Ambiguator() {
     45                 @Override
     46                 public Bundle make(Bundle preReSerialize, Bundle postReSerialize) throws Exception {
     47                     Random random = new Random(1234);
     48                     int minHash = 0;
     49                     for (String s : preReSerialize.keySet()) {
     50                         minHash = Math.min(minHash, s.hashCode());
     51                     }
     52                     for (String s : postReSerialize.keySet()) {
     53                         minHash = Math.min(minHash, s.hashCode());
     54                     }
     55 
     56                     String key;
     57                     int keyHash;
     58 
     59                     do {
     60                         key = randomString(random);
     61                         keyHash = key.hashCode();
     62                     } while (keyHash >= minHash);
     63 
     64                     padBundle(postReSerialize, preReSerialize.size() + 1, minHash, random);
     65                     padBundle(preReSerialize, postReSerialize.size() - 1, minHash, random);
     66 
     67                     String key2;
     68                     int key2Hash;
     69                     do {
     70                         key2 = makeStringToInject(postReSerialize, random);
     71                         key2Hash = key2.hashCode();
     72                     } while (key2Hash >= minHash || key2Hash <= keyHash);
     73 
     74 
     75                     Parcel parcel = Parcel.obtain();
     76 
     77                     parcel.writeInt(preReSerialize.size() + 2);
     78                     parcel.writeString(key);
     79 
     80                     parcel.writeInt(VAL_PARCELABLE);
     81                     parcel.writeString("com.android.internal.widget.VerifyCredentialResponse");
     82 
     83                     parcel.writeInt(0);
     84                     parcel.writeInt(0);
     85 
     86                     parcel.writeString(key2);
     87                     parcel.writeInt(VAL_NULL);
     88 
     89                     writeBundleSkippingHeaders(parcel, preReSerialize);
     90 
     91                     parcel.setDataPosition(0);
     92                     Bundle bundle = new Bundle();
     93                     parcelledDataField.set(bundle, parcel);
     94                     return bundle;
     95                 }
     96 
     97                 @Override
     98                 protected String makeStringToInject(Bundle stuffToInject, Random random) {
     99                     Parcel p = Parcel.obtain();
    100                     p.writeInt(0);
    101                     p.writeInt(0);
    102 
    103                     Parcel p2 = Parcel.obtain();
    104                     stuffToInject.writeToParcel(p2, 0);
    105                     int p2Len = p2.dataPosition() - BUNDLE_SKIP;
    106 
    107                     for (int i = 0; i < p2Len / 4 + 4; i++) {
    108                         int paddingVal;
    109                         if (i > 3) {
    110                             paddingVal = i;
    111                         } else {
    112                             paddingVal = random.nextInt();
    113                         }
    114                         p.writeInt(paddingVal);
    115 
    116                     }
    117 
    118                     p.appendFrom(p2, BUNDLE_SKIP, p2Len);
    119                     p2.recycle();
    120 
    121                     while (p.dataPosition() % 8 != 0) p.writeInt(0);
    122                     for (int i = 0; i < 2; i++) {
    123                         p.writeInt(0);
    124                     }
    125 
    126                     int len = p.dataPosition() / 2 - 1;
    127                     p.writeInt(0); p.writeInt(0);
    128                     p.setDataPosition(0);
    129                     p.writeInt(len);
    130                     p.writeInt(len);
    131                     p.setDataPosition(0);
    132                     String result = p.readString();
    133                     p.recycle();
    134                     return result;
    135                 }
    136             };
    137             bundle = a.make(verifyMe, useMe);
    138         }
    139 
    140         bundle = reparcel(bundle);
    141         String value1 = bundle.getString("cmd");
    142         bundle = reparcel(bundle);
    143         String value2 = bundle.getString("cmd");
    144 
    145         if (!value1.equals(value2)) {
    146             fail("String " + value1 + "!=" + value2 + " after reparceling.");
    147         }
    148     }
    149 
    150     @SuppressLint("ParcelClassLoader")
    151     private Bundle reparcel(Bundle source) {
    152         Parcel p = Parcel.obtain();
    153         p.writeBundle(source);
    154         p.setDataPosition(0);
    155         Bundle copy = p.readBundle();
    156         p.recycle();
    157         return copy;
    158     }
    159 
    160     static abstract class Ambiguator {
    161 
    162         protected static final int VAL_PARCELABLE = 4;
    163         protected static final int VAL_NULL = -1;
    164         protected static final int BUNDLE_SKIP = 12;
    165 
    166         protected final Field parcelledDataField;
    167 
    168         public Ambiguator() throws Exception {
    169             parcelledDataField = BaseBundle.class.getDeclaredField("mParcelledData");
    170             parcelledDataField.setAccessible(true);
    171         }
    172 
    173         abstract public Bundle make(Bundle preReSerialize, Bundle postReSerialize) throws Exception;
    174 
    175         abstract protected String makeStringToInject(Bundle stuffToInject, Random random);
    176 
    177         protected static void writeBundleSkippingHeaders(Parcel parcel, Bundle bundle) {
    178             Parcel p2 = Parcel.obtain();
    179             bundle.writeToParcel(p2, 0);
    180             parcel.appendFrom(p2, BUNDLE_SKIP, p2.dataPosition() - BUNDLE_SKIP);
    181             p2.recycle();
    182         }
    183 
    184         protected static String randomString(Random random) {
    185             StringBuilder b = new StringBuilder();
    186             for (int i = 0; i < 6; i++) {
    187                 b.append((char)(' ' + random.nextInt('~' - ' ' + 1)));
    188             }
    189             return b.toString();
    190         }
    191 
    192         protected static void padBundle(Bundle bundle, int size, int minHash, Random random) {
    193             while (bundle.size() < size) {
    194                 String key;
    195                 do {
    196                     key = randomString(random);
    197                 } while (key.hashCode() < minHash || bundle.containsKey(key));
    198                 bundle.putString(key, "PADDING");
    199             }
    200         }
    201     }
    202 }
    203