OWASP category: MASVS-CODE: Code Quality
Overview
Using PendingIntent.getCreator*()
or PendingIntent.getTarget*()
to determine whether to trust a PendingIntent's sender creates an
exploitation risk.
PendingIntent.getCreator*()
or
PendingIntent.getTarget*()
returns the PendingIntent's
creator, which does not always match its sender. The creator may be trusted, but
the sender should never be trusted, as the sender might be a malicious app
that acquired another app's PendingIntent using a variety of mechanisms, for
example:
- from
NotificationListenerService
- legitimate use cases that are part of the vulnerable app.
An example of a legitimate use of PendingIntent.getCreator*()
or PendingIntent.getTarget*()
would be to show
the icon of the app that will be started by the PendingIntent.
Impact
Trusting a PendingIntent's sender because you queried (and trust) the creator can lead to vulnerabilities. If an app trusts the PendingIntent's sender based on its creator, and then shares its authentication or authorization logic, then whenever the PendingIntent's sender is a malicious app, this would lead to an authentication bypass or potentially even remote code execution based on invalidated, untrusted input, depending on the implementation of the vulnerable application's code.
Mitigations
Distinguish between sender and creator
Any type of authentication or authorization logic performed when receiving a
PendingIntent must not be based on assumptions regarding the PendingIntent's
creator identified using either PendingIntent.getCreator*()
or PendingIntent.getTarget*()
.
Use alternative ways to validate callers
If you need to authenticate the caller, instead of using PendingIntent, you should use a Service or ContentProvider – both allow fetching the caller UID with Binder.getCallingUid() when you are in the context of dispatching an incoming IPC. The UID can be queried later by using PackageManager.getPackagesForUid().
Another approach, available from API level 34, would be to use BroadcastReceiver.getSentFromUid() or BroadcastReceiver.getSentFromPackage() if the sender opted in to sharing identity during broadcast using BroadcastOptions.isShareIdentityEnabled().
You should always check if the calling package has the expected signature, as sideloaded packages can have package names overlapping with ones from the Play Store.