Race Conditions on Android Custom Permissions
Android allows mobile application developers to define custom permissions in their apps. Custom permissions are often used to protect different application components, such as Activities, Services, ContentProviders, and Broadcast Receivers, from 3rd- party applications installed on the device. For example, if an organization wanted to share data between a few of its own Android applications, it could use Content Providers via custom permissions to share the data. The permission would prevent any other app on the device from accessing the app’s data unless access was specifically requested & granted. Once a custom permission is set, only applications that were granted the custom permission (usually with signature permission) can initiate Inter Process Communication (IPC) with the protected application.
Note: Custom permissions are different from default Android permissions, as they apply to the specifically to the app rather than native system components.
Protection level for Custom Permissions
Each custom permission also has a protection level defined. The protection level signifies the risk level of the android component the permission is used to protect. The figure below shows the 4 options next to “android:protectionLevel”:
- Normal: Low Risk. This is the default protection level. Automatically granted to the application upon installation. The user’s approval is not required during installation.
- Dangerous: Higher Risk than Normal. User’s approval is required before an application is granted this permission.
- Signature: Only applications that have the same signature as of the application defining the permission are granted this permission. The user’s approval is not required during installation.
- SignatureOrSystem: Only applications in the android’s system image or applications that have the same signature as of the application defining the permission are granted this permission. The user’s approval is not required during installation.
Security issues with Custom Permissions - Race Condition
Meet our safe Android app, titled DT_InfoProvider.apk, which is an application that provides information to other trusted applications. To make sure DT_InfoProvider provides the sensitive information only to trusted applications, it first defines a custom permission “com.dt.infoprovider.permission” and protects it with a “signature” protection level. The DT_InfoProvider has an Android Service that provides the sensitive information named “InformationService”. DT_InfoProvider protects InformationService with the above defined custom permission.
Based on InfoProvider’s setup, Android now ensures only applications having the “com.dt.infoprovider.permission” can perform IPC with DT_InfoProvider. Additionally since this custom permission has a protection level of “signature”, applications signed with the same key as DT_InfoProvider can only request it. See figure below where the customer permission has a protection level of signature.
The custom permission of “com.dt.infoprovider.permission” is owned by com.datatheorem.examples.custperms.provider (from the DT_InfoProvider application) and it has a protection level of 2, which is the “Signature” protection level.
Figure : Snapshot of the /data/system/packages.xml file after installing the DT_InfoProvider application.
Meet another safe Android app, titled DT_InfoConsumer.apk. DT_InfoConsumer is signed with the same key as of DT_InfoProducer and uses the custom permission “com.dt.infoprovider.permission”. When DT_InfoConsumer is installed on an Android device, notice that Android does not notify the user about the permission requested by the application (as it is a custom permission and not a system permission). DT_InfoConsumer can now bind to the “InformationService” in DT_InfoProvider and retrieve the sensitive information, as designed.
Now meet a dangerous Android app, titled Evil_InfoRetriever.apk. This is a malicious 3rd party application signed with its own unique key. Evil_InfoRetriever’s has knowledge of DT_InfoProvider and the custom permission it defines, as all custom permissions are publicly accessible. Evil_InfoRetriever now defines the exact same custom permission as DT_InfoProvider, which is com.dt.infoprovider.permission; however, it sets the protection level to “Normal” (instead of “Signature”). Then, using _
Off to the Races…
As a recap:
- Evil_InfoRetriever has enumerated the custom permissions of its target (DT_InfoProvider), which again are publicly accessible.
- Evil_InfoRetriever has set the same custom permissions of DT_InfoRetriever (com.dt.infoprovider.permission), but with “Normal” protection. Where as the original DT_InfoProvider has set the protection level to “Signature”
If the Evil_InfoRetriever app is installed on an Android device before DT_InfoProvider, the custom permission for “com.dt.infoprovider.permission” will have a protection level to “Normal” (sourced from Evil_InfoRetriever), despite the fact that DT_InfoRetriever has set it to “Signature”. In this scenario, since Evil_InfoRetriever was downloaded first, it wins the “Race” and gets to set the protection level of the custom permission before any other app. Any app that downloaded after Evil_InfoRetriever that has the same custom permission but different protection level will simply have to adhere to the protection level set by Evil_InfoRetriever. This scenario allows all 3rd-party apps to have access to the DT_InfoProvider’s components, despite the fact it has a protection level set to “signature”.
When the user installs Evil_InfoRetriever.apk the packages.xml file looks like the figure below:
The permission “com.dt.infoprovider.permission” is owned by the Evil_InfoRetriever application. Next when the user installs the DT_InfoProvider.apk the packages.xml file looks the figure below:
The previous two figures show that the DT_InfoProvider.apk is granted the “com.dt.infoprovider.permission”; however the Protection level has been downgraded from “signature” to “normal”, which allows Evil_InfoRetriever to query sensitive data from DT_InfoProvider.
As a proof of concept, the source code for all apps are provide in Data Theorem’s GIT repository (https://github.com/datatheorem). When performing the test, ensure to sign the DT_InfoProvider and DT_InfoConsumer apps with one key and Evil_InfoRetreiver application with another key.