During the development of an Android application, the developer must specify certain configurations in the AndroidManifest.xml file that the application will require, actions that the application can perform and general other activities related to the application’s functioning. During the build process, the manifest file is transformed from plaintext XML to binary XML by the development environment, such as Eclipse or Android Studio. This file contains a smorgasbord of information about the application, including the following:

  • Unique package name (e.g.,com.android.androlab) and version information
  • Minimum Android and SDK version required to run the program
  • Permissions the application needs
  • List of all Activities, Services, Broadcast Receivers, Content Providers etc.
  • Hardware / Software features needed

An example of AndroidManifest.xml file is given below:

<?xml version="1.0" encoding="utf-8"?>
<manifest android:sharedUserId="com.android.lab" android:versionCode="1337" android:versionName="androlabpro"
android:compileSdkVersion="28" android:compileSdkVersionCodename="9" package="com.android.lab.androlab"
platformBuildVersionCode="28" platformBuildVersionName="9"
  xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="28" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
    <uses-permission android:name="com.airwatch.sdk.BROADCAST" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-feature android:glEsVersion="0x00020000" android:required="true" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <application android:theme="@style/Theme.AppCompat.Light.NoActionBar.FullScreen" android:label="AndroLab" android:icon="@mipmap/ic_launcher" android:name="com.android.lab.androlab.BaseApplication" android:allowBackup="true" android:debuggable="true" android:supportsRtl="true" android:extractNativeLibs="false" android:roundIcon="@mipmap/ic_launcher_round" android:appComponentFactory="androidx.core.app.CoreComponentFactory">
    <activity android:name="com.android.lab.androlab.MainActivity">
       <intent-filter>
       <action android:name="android.intent.action.MAIN"/>
       <category android:name="android.intent.category.LAUNCHER"/>
       </intent-filter>
    </activity> 
    <activity android:name=".PaymentDetailsActivity" android:exported="true" />  
    </application>
</manifest>

From security standpoint, the manifest file is usually the first item that a penetration tester will inspect on an engagement. Let’s try to identify the following information from the above manifest file.

ChecklistFindings
App Package Nameandrolab package="com.android.lab.androlab"
Application NameAndroLab android:label="AndroLab"
SharedUserIDlab android:sharedUserId="com.android.lab"
App Version Code1337 android:versionCode="1337"
App Version Nameandrolabpro android:versionName="androlabpro"
Complied SDK Version28 android:compileSdkVersion="28"
Target SDK Version28 android:targetSdkVersion="28"
Minimum SDK Version24 android:minSdkVersion="24"
Total Permission Elements18 uses-permission
Total Features Request3 uses-feature
Backup EnabledYes android:allowBackup="true"
Export EnabledYes android:exported="true"
Debug Mode EnabledYes android:debuggable="true"

Application Components

Each Android application is composed of the following major components:

  • Intents: Intents are message objects that contain information about an operation to be performed, the optional target component on which to act, and additional flags or other supporting information (which may be significant to the recipient).
 <uses-permission android:name="com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE" />
...
...
...
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<action android:name="android.intent.action.PACKAGE_CHANGED" />
<action android:name="android.intent.action.PACKAGE_RESTARTED" />

There are two types of Intent Filters:

  1. Explicit Intents: These Intent Filters detail the application that will satisfy the Intent by providing the target application’s package name or a fully-qualified component class name. Explicit Intents are generally used to start a component within the application itself. For instance, a user trying to download a file within an app would trigger an Activity in the visible app that would start a Service to download a file in the background via Explicit Intent between the Activity and the Service.
  2. Implicit Intents: These Intents Filters do not name a specific component, but instead declare a general action to perform. This allows a component from another app to handle the component. For example, if the app wants to show a user a specific location on a map it could use an implicit intent to request another capable application to show this location.
  • Activities: Activities are the graphical user interface components with which the user interacts while using the application. Lower-level management of Activities is handled by the appropriately named Activity Manager service, which also processes Intents that are sent to invoke Activities between or even within applications.
<activity android:name="com.android.androlab.ui.home.HomeActivity" android:launchMode="singleTop" android:screenOrientation="portrait">
  <intent-filter>
   <action android:name="android.intent.action.MAIN" />
   <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
</activity>
<activity android:name="com.android.androlab.ui.home.view.QRReaderActivity" android:launchMode="singleTask" />
  • Services: Services are background processes that manage the application’s core functions. The SmsReceiverService and the BluetoothOppService are two examples of typical Android services. Although each of these services operates in the background and is not visible to the user, they, like other Android app components, may use IPC capabilities by sending and receiving Intents.
<service android:name="com.google.android.gms.measurement.AppMeasurementService" android:enabled="true" android:exported="false" />
<service android:name="com.google.android.gms.measurement.AppMeasurementJobService" android:permission="android.permission.BIND_JOB_SERVICE" android:enabled="true" android:exported="false" />
  • Broadcast Receivers: Broadcast Receivers are components that monitor the system for messages or broadcast announcements. These are frequently used when applications want to receive an implicit Intent matching specific additional conditions. Broadcast Receivers may also be registered programmatically at runtime by using the registerReceiver method. This method can also be overloaded to set permission restrictions on the receiver. Setting permission requirements on Broadcast Receivers can limit which applications can send Intents to that endpoint.
<receiver android:name="com.android.lab.receivers.BootCompleteEventReceiver" android:exported="true">
  <intent-filter>
    <action android:name="android.intent.action.BOOT_COMPLETED" />
   </intent-filter>
</receiver>
  • Content Providers: Content Providers act as a structured interface to common, shared data stores. For example, the Contacts provider and Calendar provider manage centralized repositories of contact entries and calendar entries, respectively, which can be accessed by other applications (with appropriate permissions). In any chat application, that allows file or picture attachments has an attachment icon that may be clicked to display an explorer window with a list of all available files (in your devices). When you click on one, it is automatically attached to that message. That is, after all, the duty of (one of the) content providers.
<provider android:name="com.google.firebase.provider.FirebaseInitProvider" android:exported="false" android:authorities="com.augmedix.phone.prod.firebaseinitprovider" android:initOrder="100" android:directBootAware="true" />

Permissions and Their Types

Permissions are used to control access to certain Android components, such as activities, broadcast receivers, services, and content providers. A developer can define several permission types in the AndroidManifest.xml file. At runtime, permissions are also used to determine if the application has the rights to access sensitive data or do risky activities. For instance, in order for an app to gain access to internet, the developer must add the following statement in the AndroidManifest.xml file:

<uses-permission android:name="android.permission.INTERNET" />

The android:protectionLevel attribute defines the procedure that the system should follow before grants the permission to the application that has requested it. There are four values that can be used with this attribute:

  • normal (0×0) – the default value for a permission.
  • dangerous (0×1) – indicates that this permission has the ability to access some potentially sensitive information or perform actions on the device.
  • signature (0×2) – indicates that this permission can only be granted to another application that was signed with the same certificate as the application that defined the permission.
  • signatureOrSystem (0×3) – this is the same as the signature protection level, except that the permission can also be requested by an application that came with the Android system image.
<permission android:name="com.androlab.READ_CONTACTS" android:protectionLevel="dangerous" />

Compliance and Best Practices

1. Review Application Backup

When application backup is enabled, local data from your application can be exported to Google Cloud or to an external device via adb backup. By default application backup is enabled and it includes:

  • Shared preferences files
  • Files saved in one of the paths returned by
    • getDatabasePath(String)
    • getFilesDir()
    • getDir(String, int)
    • getExternalFilesDir(String)
#Noncompliant
<application
  android:allowBackup="true">
</application>
#Compliant
<application
  android:allowBackup="false">
</application>

Recommendation

  • Disable application backup unless it’s required for your application to work properly.
  • Narrow the scope of backed-up files by using either
    • backup rules (see android:fullBackupContent attribute)
    • a custom BackupAgent
    • or the dedicated “no_backup” folder (see android.content.Context#getNoBackupFilesDir()).
  • Don’t backup local data containing sensitive information unless they are properly encrypted.
  • Make sure that the keys used to encrypt backup data are not included in the backup.
  • Validate data from backed-up files. They should be considered untrusted as they could have been restored from an untrusted source.

2. Disable Debug Feature

Setting the debuggable attribute to true in an android application’s manifest element may present a security risk. It’s more easy to perform reverse engineering and inject arbitrary code in the context of a debuggable application. Android apps that are not in the production stage are intended to have this property set to true to aid developers, however this attribute should be set to false prior to the production release.

#NonCompliant
<application
android:debuggable="true"
</application>
#Compliant
<application
android:debuggable="false"
</application>

3. Access Restriction to Broadcasted Intents

It is important to consider broadcasts as untrusted and to limit the applications that can send broadcasts to the receiver. Permissions can be specified to restrict broadcasts to authorized applications. Restrictions can be enforced by both the sender and receiver of a broadcast. If permissions are specified when registering a broadcast receiver, then only broadcasters who were granted this permission can send a message to the receiver.

Related CVE: CVE-2019-1677, CVE-2015-1275

#NonCompliant
<receiver android:name=".MyBroadcastReceiver" android:exported="true">  <!-- Sensitive -->
    <intent-filter>
        <action android:name="android.intent.action.AIRPLANE_MODE"/>
    </intent-filter>
</receiver>
#Compliant
<receiver android:name=".MyBroadcastReceiver"
    android:permission="android.permission.SEND_SMS"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.AIRPLANE_MODE"/>
    </intent-filter>
</receiver>

4. Access Restriction to exported components with appropriate permissions

If an Android component is exported without defining its permissions, other mobile applications can interact with it and potentially perform unauthorized actions. For example, if no permissions are set, an exported content provider may expose sensitive data to other mobile apps. It’s strongly recommended to implement restrictive permissions on exposed components.

#NonCompliant
<provider
  android:authorities="com.example.app.Provider"
  android:name="com.example.app.Provider"
  android:exported="true"
  android:readPermission="com.example.app.READ_PERMISSION"
  android:writePermission="com.example.app.WRITE_PERMISSION" />

<activity android:name="com.example.activity.Activity">
  <intent-filter>
    <action android:name="com.example.OPEN_UI"/>
    <category android:name="android.intent.category.DEFAULT"/>
  </intent-filter>
</activity>
#Compliant
<provider
  android:authorities="com.example.app.Provider"
  android:name="com.example.app.Provider"
  android:exported="true"
  android:readPermission="com.example.app.READ_PERMISSION"
  android:readPermission="com.example.app.WRITE_PERMISSION" />

<activity android:name="com.example.activity.Activity"
          android:permission="com.example.app.PERMISSION">
  <intent-filter>
    <action android:name="com.example.OPEN_UI"/>
    <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>
</activity>