1

Focusing on user privacy and security and paying for trust with responsibility, OPPO established the ZIWU Cyber Security Lab. With the mission of "protecting users' security and privacy, and injecting security genes into the brand", the laboratory continues to focus on and focus on business security, red-blue confrontation, IoT security, Android security, data and privacy protection and other fields.

This article comes from the OPPO Meridian Internet Security Lab.

1 Universal use of unsafe PendingIntent

1.1 Characteristics of unsafe PendingIntent

So far, we have solved the first problem of this topic. After research, it is shown that most of the PendingIntent used in the Android system can be obtained by third-party apps.

The acquisition methods include bind SliceProvider, listen for notifications, connect to the media browser service, or bind the AppWidgetsProvider that holds the widget.

Thus, the second key question of topic research is introduced: If these PendigIntents are not secure, how can they be exploited to cause security hazards?

First, we need to identify what kind of PendingIntent is unsafe. The public vulnerability cases described above are all hijacking the broadcast PendingIntent whose base Intent is an empty Intent.

The developer documentation before Android 12 also raised a security warning about the PendingIntent whose base Intent is an implicit Intent, but did not clearly tell what kind of harm there is. And in AOSP code and popular apps, the following code patterns exist widely. This makes us wonder, is there really a problem with the PendingIntent built by the Implicit base Intent? Only by finding a definite exploit method for this PendingIntent can we really prove the existence of a security problem.

1.2 In-depth Intent fillIn rewriting mechanism

Before finding the utilization method, it is necessary to deeply explore the rewriting mechanism of PendingIntent, which determines how to rewrite the base Intent after other apps obtain the PendingIntent. This mechanism is provided by the Intent.fillIn function:


In the above code, this object points to the current Intent, and other is another Intent. If the member variable in the current Intent is empty, it can be overwritten by the corresponding member variable in other. What is special is the component and selector members in the Intent. Even if the component and selector in the current Intent are empty, they cannot be overwritten by other unless PendingIntent
FILL_IN_COMPONENT is set
Or the FILL_IN_SELECTOR flag.

1.3 PendingIntent redirection attack

Therefore, after obtaining the PendingIntent, the action, category, data, clidpdata, package, flag, extra and other members of the base Intent may be rewritten, but the component and selector cannot be rewritten, as shown in the figure. In particular, for the case where the base Intent is an implicit Intent, the action has already been set, so it cannot be rewritten. The attacker cannot hijack the PendingIntent and re-add the action in the base Intent like the previous Android system broadcastAnyWhere vulnerability. Implicitly opens a protected component.

Figure Intent member

Here comes the key point of problem solving. Since the package can be specified, recalling the previous exploit method in the Intent Bridge vulnerability, we can solve this problem subtly by setting the flag in the intent. Intent provides flags about temporary authorization:

  • FLAG_GRANT_READ_URI_PERMISSION: When the Intent carries this flag, the receiver of the Intent will obtain the read permission in the data URI carried by the Intent and the clipdata URI
  • FLAG_GRANT_WRITE_URI_PERMISSION: When the Intent carries this flag, the receiver of the Intent will get the data URI carried by the Intent and the write permission in the clipdata URI

In short, the malicious App redirects the PendingIntent to the malicious App itself. By modifying part of the PendingIntent base Intent (modifying the package name, authorization flag, and data/clipdata), it enables it to open the malicious App itself with the permission of the victim App. , so that the malicious app can obtain read and write permissions to the private data of the victim app the moment it is opened. The specific use method is shown in the figure:

Figure PendingIntent redirect attack

Proceed as follows:
1. The victim App builds a PendingIntent through getActivity and uses it in notifications, SliceProvider, and widgets, assuming that its base Intent is an implicit Intent;

2. The attacking App obtains the PendingIntent of the victim App through the various channels discussed above;

3. Attack the App to modify the base Intent in the PendingIntent. Since it is an implicit Intent, the action, component and selector cannot be modified. But it can be modified as follows:

  • Modify data or clidpdata so that its URI points to the private ContentProvider of the victim App;
  • Modify the package to point to the attacking App;
  • Added FLAG_GRANT_READ_URI_PERMISSION and FLAG_GRANT_WRITE_URI_PERMISSION flags.

At the same time, the attacking App declares that an Activity supports implicit startup, and its Intent-filter is consistent with the action in the base Intent.

4. The attacking App calls PendingIntent.send;

5. Since this PendingIntent represents the identity and permissions of the victim App, the modified base Intent will be sent in the name of the victim App to open the Activity of the attacking App;

6. At the moment when the attacking App Activity is opened, that is, it is authorized to access the URI carried in the base Intent, and it also obtains the read and write permission to the private ContentProvider of the victim App.

The private ContentProvider of the victim App above needs to carry the attribute grantUriPermission=true, which is not limited to the ContentProvider of the victim App, but also the ContentProvider that the victim App has permission to access. A common ContentProvider with the attribute grantUriPermission=true on the mobile phone is the Contacts Provider representing the address book. As long as the victim App has the READ_CONTACTS permission, such a PendingIntent vulnerability will lead to the disclosure of the address book.

In this way, through the above 6 steps, we can successfully implement the vulnerability exploitation of implicit Intent to construct PendingIntent, read and write the private data of victim App, which also solves the second key problem raised in this research: through implicit Intent The constructed PendingIntent is vulnerable to the generic redirect privilege escalation attack and is also insecure.

Since the trick of grantUri is used here, it does not apply to broadcast PendingIntent, because broadcast receivers cannot be grantedUri. In addition, since Android 5.0, the Service cannot be started implicitly, so it is difficult to see the service PendingIntent whose base Intent is an implicit Intent. Therefore, the PendingIntent redirection attack here is mainly applicable to Activity PendingIntent.

2 Real cases in Android system

Surprisingly, ActivityPendingIntent built by implicit Intent exists widely in AOSP code and popular apps before Android 12. The following are typical cases we found, which may lead to the leakage of sensitive information of mobile phones and even execution with the permission of the victim app. arbitrary code. These vulnerability cases have been fixed by the vendor.

Typical case of unsafe PendingIntent

2.1 CVE-2020-0188

Unsafe PendingIntent exists in AOSP SettingsSliceProvider,
Once the SettingsSliceProvider is blinded,
The returned Slice will carry a noOpIntentPendingIntent that does nothing:

The attacking App obtains the PendingIntent through bind SettingsSliceProvider, modifies the base Intent and sends it with the permissions of Settings, and waits for its own Activity to be opened, then it can read and write some private Content Providers of Settings. as the picture shows:

Figure CVE-202-0188 POC

2.2 CVE-2020-0389

Unsafe PendingIntent exists in
In the AOSP SystemUI RecordingService, it is used for the notification sent after the user's screen recording is successfully saved.


Malicious apps can implement a NotificationListener
Service, modify the base Intent, and point its clipdata to ContactsProvider:

Since SystemUI has the READ_CONTACTS permission, the address book can be successfully read when the malicious app is opened.

2.3 A-166126300

Unsafe PendingIntent exists in AOSP BluetoothMediaBrowserService

Malicious App can connect to BluetoothMediaBrowserService and obtain PendingIntent through MediaBrowserCompat.ConnectionCallback.
Since the BluetoothMediaBrowserService exists in the Bluetooth application with address book permission, the address book can be read through the PendingIntent redirection attack

2.4 A popular app

A popular app with address book permissions implements a widget, and the user clicks the button of the widget to jump, but this jump is realized by constructing a PendingIntent with an implicit Intent.

Bind the AppWidgetProvider to which the widget belongs, obtain RemoteViews->mActions->mResponse->mPendingIntent successively through reflection, and finally get the above unsafe PendingIntent, and then do the same to read the address book.

2.5 CVE-2020-0294

These unsafe PendingIntents exist in Android system services. For some bind services, the system provides PendingIntents to jump to the service management interface.

These PendingIntents can be obtained directly through the system APIActivityManager.getRunningServiceControlPanel, followed by PendingIntent redirection to read the protection ContentProvider in Settings.

2.6 Hazards

Many of the above cases can lead to the leakage of sensitive personal information such as address books, but in fact, since the PendingIntent redirection attack also has the ability to write data, it may cause greater harm.

For example, many apps have a hot update function. Generally, files such as dex/jar/apk/so are placed in their own private directories. If these private directories can be referenced by the ContentProvider with grantUriPermission=true, the PendingIntent redirection attack can be used. To rewrite the hot update file, inject the attacker's own code into it, and execute arbitrary code with the permission of the victim App.

For PendingIntents such as CVE-2020-0188 and CVE-2020-0294 that originate from the system uid, due to restrictions in UriGrantsManagerService, the harm in the native system is very limited, and only a few specific Content Providers can be read.

However, due to the customization of the Android system, the above restrictions may be broken in OEM manufacturers, causing greater harm.

Google's fix for this type of vulnerability in Android initially set the base Intent as an explicit Intent, specifying explicit components. Later, FLAG_IMMUTABLE was used to fix it. When this flag is used, the base Intent of PendingIntent cannot be rewritten through the Intent.fillIn function, for example

3 Automated Analysis

Based on the mastery of the unsafe PendingIntent features, we wrote an automated scanning tool, PendingIntentScan, which performs static data flow analysis on apk based on the Java static analysis framework Soot[4]. Its architecture is shown in the figure.

Figure PendingIntentScan principle

First, use Soot to convert the apk's bytecode to Jimple's IR, then search for a series of APIs that generate PendingIntent, and pick out the ones that don't use FLAG_IMMUTABLE:

Then, check the Intent parameter of PendingIntent through ForwardFlowAnalysis provided by Soot to see if the following function is called. If neither is used, the PendingIntent is considered unsafe:

This tool is currently open source in
https://github.com/h0rd7/PendingIntentScan , you can quickly find the unsafe PendingIntent in the apk, the effect is as follows.

4 Android 12 security changes

In response to our research results, the Google Android security team conducted a comprehensive investigation of the AOSP code and fixed almost all unsafe PendingIntents. Most fixes use PendingIntent.FLAG_IMMUTABLE, a small number of fixes set the base Intent to an explicit Intent.

In the Android 12 major version, the Android system has made major security changes to the behavior of PendingIntent, and introduced a new flag: PendingIntent.FLAG_MUTABLE, indicating that the base Intent can be rewritten. This together with the original FLAG_IMMUTABLE describes the variability of the PendingIntent.

For Target S+ apps, the Android system requires developers to explicitly specify the variability of PendingIntent, and either FLAG_IMMUTABLE or FLAG_MUTABLE must be used, otherwise the system will throw an exception. This requires developers to have a clear understanding of the use of their PendingIntent and know whether PendingIntent will be rewritten in the future.

Google also has detailed secure coding recommendations for developers:

  • Use FLAG_IMMUTABLE whenever possible to generate an immutable PendingIntent;
  • If FLAG_MUTABLE is used to generate an overridable PendingIntent, the base Intent must use an explicit Intent, which explicitly specifies the component of the Intent.

At the same time, a new lint check plugin PendingIntentMutableFlagDetector is also introduced in AndroidStuido IDE to check whether PendingIntent uses FLAG_IMMUTABLE.

5 Conclusion

This topic solves the acquisition problem of PendingIntent, clarifies the characteristics of unsafe PendingIntent, and proposes the redirection attack utilization method of unsafe PendingIntent, thereby revealing a general security risk related to the use of PendingIntent in Android system and popular apps. Google has fixed the vulnerabilities described in the issue, and introduced significant security changes to mitigate this issue in Android 12, with detailed secure coding recommendations for developers.

Developers should be extra careful when using FLAG_IMMUTABLE to construct a PendingIntent. In addition to using explicit Intent, it is also necessary to ensure that other unfilled fields of the base Intent will not cause security implications. For example, the problematic code below is from an example of a real app. This PendingIntent has set an explicit Intent, which is used in the notification to start the MainActivity that is not exported internally

In MainActivity,
You can process EXTRA_REDIRECT_INTENT and finally call startActivity:

In this way, hijacking PendingIntent can still set EXTRA_REDIRECT_INTENT, and open any protection component of the application through startActivity.

Therefore, every PendingIntent that does not use FLAG_IMMUTABLE should be carefully reviewed, this is our last security advice to developers.

6. Reference

[1]http://retme.net/index.php/2014/11/14/broadAnywhere-bug-17356824.html

[2]https://www.slideshare.net/CanSecWest/csw2016-chaykin-havingfunwithsecuremessengersandandroidwear

[3]https://mp.weixin.qq.com/s/SAhXsCHvAct_2SxCXd2w0Q

[4] http://soot-oss.github.io/soot/

[5]https://developer.android.com/about/versions/12/behavior-changes-12#pending-intent-mutability

About the author

heeeeen security architect

Graduated from Beijing University of Aeronautics and Astronautics, now working in OPPO Meridian Internet Security Laboratory, good at Android framework and APP vulnerability mining, and has received Google security thanks for many times

For more exciting content, please scan the code and follow the [OPPO Digital Intelligence Technology] public account


OPPO数智技术
612 声望952 粉丝