Transmission of sensitive information in BroadcastReceiver

  CRITICAL  
Detection method   DAST         SENSITIVE INFO  

Description

An application puts sensitive information into an Intent to launch BroadcastReceiver. This can lead to interception of information by external applications.

Interprocess communication (IPC) on Android is conducted using a special object—Intent. Parameters of Intent handlers are set in the main file of the application manifest—AndroidManifest.xml, or, in case of dynamic BroadcastReceivers, in the application's code. If an implicit Intent is used (the recipient is not specified explicitly or the broadcasting mechanism Broadcast is used), the data in such message could be compromised. Moreover, malicious applications could use mechanisms of delegation of process control, such as implicit calls to application components or objects like PendingIntent, for interception of control and fishing attacks.

The following object types are dangerous: ActivityServiceBroadcastReceiver and ContentProvider, because they are open to communication with other applications and don't belong to system Android calls (such as android.intent.action.MAIN).  BroadcastReceiver is, by default, open to interaction with other applications, so the interception of control or of an Intent with confidential information is possible.

Recommendations

By sending a Broadcast containing sensitive information into an internal BroadcastReceiver, explicit Intent, Private BroadcastReceiver or LocalBroadcastManager should be used. Note, that an application must not include sensitive information into a public Broadcast.

To receive a Broadcast you need to create BroadcastReceiver. Risks from using BroadcastReceiver and corresponding countermeasures vary depending on the type of a Broadcast.

To find out what type of BroadcastReceiver you are supposed to create, follow through the table and chart below. An application that receives a Broadcast is not able to check the name of the package from which the Broadcast has been sent, so it is not possible to create a partner BroadcastReceiver, (should be corrected) as is the case with Activity.

Type of BroadcastReceiver

Description

Private BroadcastReceiver

BroadcastReceiver that can receive a Broadcast only from the same application, and is therefore the safest type

Public BroadcastReceiver

BroadcastReceiver that can receive a Broadcast from any application

In-house BroadcastReceiver

BroadcastReceiver that can receive a Broadcast only from other applications of the same developer

 

Picture 1

 

In addition, BroadcastReceiver can be static or dynamic depending on the definition method.

 

Definition method

Characteristics

Static BroadcastReceiver

By adding <receiver> elements into AndroidManifest.xml

  • is not able to receive some system Broadcasts (such as ACTION_BATTERY_CHANGE)
  • will receive Broadcasts starting from initial boot of the application until its uninstallation

Dynamic BroadcastReceiver

By calling a registerReceiver() method

  • can receive all Broadcasts, even those not receivable by static BroadcastReceiver
  • the period of receiving Broadcasts is controlled by the program logic
  • private BroadcastReceiver cannot be created

Example: creation of Private BroadcastReceiver

Private BroadcastReceiver is the safest type, because it can receive a Broadcast from the same application only. Private BroadcastReceiver can be defined as Static only.

 

Rules (receiving a Broadcast):

1. Explicitly set the "exported" attribute to "false"

2. Verify the received Intent and handle it in a secure manner despite the fact that it was sent from within the same application

3. You can put confidential information into the resulting Intent because it is sent and received within the same application

 

Definition of the component in AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.mobix.android.broadcast.privatereceiver" >
         <application
                  android:icon="@drawable/ic_launcher"
                  android:label="@string/app_name"
                  android:allowBackup="false" >
                  <!-- Private Broadcast Receiver -->
                  <!-- *** 1 *** Explicitly set the "exported" attribute to "false" -->
                  <receiver
                           android:name=".PrivateReceiver"
                           android:exported="false" />
                  <activity
                           android:name=".PrivateSenderActivity"
                           android:label="@string/app_name"
                           android:exported="true" >
                           <intent-filter>
                                    <action android:name="android.intent.action.MAIN" />
                                    <category android:name="android.intent.category.LAUNCHER" />
                           </intent-filter>
                  </activity>
         </application>
</manifest>

Receiving a Broadcast

package com.mobix.android.broadcast.privatereceiver;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class PrivateReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
                // *** 2 *** Verify the received Intent and handle it in a secure manner despite the fact that it was sent from within the same application
                String param = intent.getStringExtra("PARAM");
                Toast.makeText(context,
                                String.format("Received param: \"%s\"", param),
                                Toast.LENGTH_SHORT).show();
                // *** 3 *** You can put confidential information into the resulting Intent because it is sent and received within the same application
                setResultCode(Activity.RESULT_OK);
                setResultData("Sensitive information");
                abortBroadcast();
        }
}

Rules (sending a Broadcast):

1. Use an explicit Intent indicating the name of BroadcastReceiver class within the application

2. You can send sensitive information

3. Verify the received result data and handle it in a secure manner despite the fact that it was sent from the BroadcastReceiver of the same application

 

package com.mobix.android.broadcast.privatereceiver;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class PrivateSenderActivity extends Activity {
        public void onSendNormalClick(View view) {
                // *** 1 *** Use an explicit Intent indicating the name of BroadcastReceiver class within the application
                Intent intent = new Intent(this, PrivateReceiver.class);
                // *** 2 *** You can send sensitive information
                intent.putExtra("PARAM", "Sensitive information from sender");
                sendBroadcast(intent);
        }
        public void onSendOrderedClick(View view) {
                // *** 1 *** Use an explicit Intent indicating the name of BroadcastReceiver class within the application
                Intent intent = new Intent(this, PrivateReceiver.class);
                // *** 2 *** You can send sensitive information
                intent.putExtra("PARAM", "Sensitive information from sender");
                sendOrderedBroadcast(intent, null, mResultReceiver, null, 0, null, null);
        }
private BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
                // *** 3 *** Verify the received result data and handle it in a secure manner despite the fact that it was sent from the BroadcastReceiver of the same application
                // See section "Secure Handling of the Input Data"
  
                String data = getResultData();
                PrivateSenderActivity.this.logLine(
                                String.format("Received result: \"%s\"", data));
        }
};
        private TextView mLogView;
        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.main);
                mLogView = (TextView)findViewById(R.id.logview);
        }
        private void logLine(String line) {
                mLogView.append(line);
                mLogView.append("\n");
        }
}

Links

  1. https://developer.android.com/guide/components/intents-filters.html
  2. https://developer.android.com/training/basics/intents/index.html
  3. https://cwe.mitre.org/data/definitions/927.html
  4. https://github.com/OWASP/owasp-mstg/blob/master/Document/0x05h-Testing-Platform-Interaction.md#testing-for-injection-flaws-mstg-platform-2