• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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_2023_20918_test;
18 
19 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
20 
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assume.assumeNoException;
23 
24 import android.app.PendingIntent;
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.net.Uri;
30 
31 import androidx.test.runner.AndroidJUnit4;
32 
33 import org.junit.Test;
34 import org.junit.runner.RunWith;
35 
36 import java.io.OutputStream;
37 import java.nio.charset.StandardCharsets;
38 import java.util.concurrent.CompletableFuture;
39 import java.util.concurrent.TimeUnit;
40 
41 @RunWith(AndroidJUnit4.class)
42 public class DeviceTest {
43     private static final long TIMEOUT_MS = 10_000L;
44     private String mAssumeFailMsg;
45 
46     @Test
testCVE_2023_20918()47     public void testCVE_2023_20918() {
48         try {
49             Context context = getApplicationContext();
50             mAssumeFailMsg = context.getString(R.string.msgAssumeFailDefault);
51             final CompletableFuture<Boolean> exploitActivityReturn = new CompletableFuture<>();
52             final String bcastActionFail = context.getString(R.string.bcastActionTestFail);
53             final String bcastActionPass = context.getString(R.string.bcastActionTestPass);
54             final String bcastActionAssumeFail =
55                     context.getString(R.string.bcastActionTestAssumeFail);
56 
57             // Register a broadcast receiver to receive broadcast from ExploitActivity indicating
58             // presence of vulnerability
59             BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
60                 @Override
61                 public void onReceive(Context context, Intent intent) {
62                     try {
63                         if (intent.getAction().equals(bcastActionFail)) {
64                             exploitActivityReturn.complete(true);
65                         } else if (intent.getAction().equals(bcastActionPass)) {
66                             exploitActivityReturn.complete(false);
67                         } else if (intent.getAction().equals(bcastActionAssumeFail)) {
68                             // mAssumeFailMsg set here is used in assumeNoException() triggered
69                             // when exploitActivityReturn.get() raises a timeout exception
70                             mAssumeFailMsg = intent
71                                     .getStringExtra(context.getString(R.string.keyMsgAssumeFail));
72                         }
73                     } catch (Exception ignored) {
74                         // ignore the exceptions
75                     }
76                 }
77             };
78             IntentFilter filter = new IntentFilter();
79             filter.addAction(bcastActionFail);
80             filter.addAction(bcastActionPass);
81             filter.addAction(bcastActionAssumeFail);
82             context.registerReceiver(broadcastReceiver, filter);
83 
84             // Write some data to the Uri content://authority/file_path/poc.txt
85             final String uriString = context.getString(R.string.contentUri);
86             try (OutputStream outputStream =
87                     context.getContentResolver().openOutputStream(Uri.parse(uriString));) {
88                 outputStream.write(
89                         context.getString(R.string.fileContents).getBytes(StandardCharsets.UTF_8));
90             }
91 
92             // Creating an intent to launch ExploitActivity
93             Intent intent = new Intent();
94             final String attackerPkg = context.getString(R.string.pkgAttacker);
95             final String exploitActivity = context.getString(R.string.activityExploit);
96             intent.setClassName(attackerPkg, exploitActivity);
97             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
98 
99             // Creating the inner intent for PendingIntent
100             Intent innerIntent = new Intent(Intent.ACTION_MAIN, Uri.parse(uriString));
101             innerIntent.setClassName(attackerPkg, exploitActivity);
102             innerIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
103             innerIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
104 
105             // Launch the ExploitActivity passing PendingIntent as data
106             intent.putExtra(context.getString(R.string.keyPendingIntent), PendingIntent
107                     .getActivity(context, 0, innerIntent, PendingIntent.FLAG_IMMUTABLE));
108             context.startActivity(intent);
109 
110             // On vulnerable device, the PendingIntent launchIntentFlags will be added even though
111             // it is immutable, so the test should fail if the flags are found to take effect.
112             assertFalse(context.getString(R.string.msgFail),
113                     exploitActivityReturn.get(TIMEOUT_MS, TimeUnit.MILLISECONDS));
114         } catch (Exception e) {
115             assumeNoException(mAssumeFailMsg, e);
116         }
117     }
118 }
119