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