Android System Securing, Hardening, & Privacy

Reverse Engineering Android Broadcast Receivers for IPC Security Flaws

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction to Android IPC and Broadcast Receivers

Android’s component model is fundamental to its architecture, allowing applications to be built from loosely coupled components. Inter-Process Communication (IPC) is crucial for these components, enabling them to interact across different processes, often belonging to different applications. Among the various IPC mechanisms—Intents, Binders, Content Providers, and Services—Broadcast Receivers play a pivotal role. They allow applications to listen for and respond to system-wide or application-specific broadcast messages, acting as a publish-subscribe communication model.

Broadcast Receivers are essential for scenarios like reacting to system events (e.g., battery low, boot completed), receiving notifications from other apps, or even synchronizing data. However, their pervasive nature and potential for inter-app communication make them a common attack surface if not implemented or configured securely. Understanding how to reverse engineer and analyze Broadcast Receivers is therefore a critical skill for mobile security researchers and penetration testers aiming to uncover IPC vulnerabilities.

The Security Implications of Broadcast Receivers

Misconfigurations or insecure implementations of Broadcast Receivers can lead to a variety of severe security flaws, including:

  • Privilege Escalation: A low-privileged malicious app could trigger a broadcast receiver in a higher-privileged app to perform sensitive actions it wouldn’t normally be allowed to do.
  • Data Theft/Exposure: If a receiver processes untrusted input or broadcasts sensitive data without proper protections, an attacker could either extract confidential information or inject malicious data.
  • Denial of Service (DoS): Sending malformed or excessively large intents to a receiver can cause the target application to crash, become unresponsive, or consume excessive resources.
  • Unauthorized Access: An exported receiver without appropriate permission checks can be invoked by any application, potentially bypassing intended security controls.

These vulnerabilities often stem from insufficient permission checks, improper validation of incoming intent extras, or allowing unrestricted access to sensitive broadcast actions.

Methodology for Reverse Engineering Broadcast Receivers

Step 1: Obtain and Decompile the APK

The first step in reverse engineering any Android application is to obtain its APK file. This can be done by extracting it from an installed application on a rooted device, downloading it from public repositories like APKMirror, or using tools like `adb pull` from a debuggable app. Once you have the APK, you need to decompile it to human-readable code.

# Pull an APK from a device (requires app package name)adb shell pm path com.example.vulnerableappadb pull /data/app/com.example.vulnerableapp-1/base.apk# Decompile the APK using apktool (to get AndroidManifest.xml and Smali code)apktool d com.example.vulnerableapp.apk# Alternatively, use JADX-GUI for Java source code (graphical tool)

Tools like `apktool` are excellent for extracting resources and the `AndroidManifest.xml`, along with converting DEX bytecode to Smali. For more readable Java code, `JADX-GUI` is often preferred, though it might sometimes miss specific low-level details found in Smali.

Step 2: Identify Broadcast Receivers in AndroidManifest.xml

After decompilation, the `AndroidManifest.xml` file is your primary source for identifying declared Broadcast Receivers. Look for the <receiver> tag within the XML structure. Key attributes to pay attention to are:

  • android:name: Specifies the class that implements the Broadcast Receiver. This is crucial for subsequent code analysis.
  • android:exported: A boolean value indicating whether the receiver can receive broadcasts from components outside the application. If true, any application can send an intent to this receiver. If false, only components within the same application or applications with the same user ID can send broadcasts. By default, a receiver is exported if it has an intent filter, and not exported if it does not.
  • android:permission: Specifies a permission that a sender must hold to send a broadcast to this receiver. This is a critical security control.
  • <intent-filter>: Defines the types of intents the receiver can respond to, including actions and categories.

Example AndroidManifest.xml snippet:

<application ...>    <receiver        android:name=".VulnerableReceiver"        android:enabled="true"        android:exported="true"        android:permission="com.example.PERMISSION_SEND_VULN_ACTION">        <intent-filter>            <action android:name="com.example.ACTION_VULNERABLE_COMMAND" />            <category android:name="android.intent.category.DEFAULT" />        </intent-filter>    </receiver>    <receiver        android:name=".InternalReceiver"        android:exported="false">        <intent-filter>            <action android:name="com.example.ACTION_INTERNAL_UPDATE" />        </intent-filter>    </receiver></application>

In this example, VulnerableReceiver is exported and requires a custom permission. InternalReceiver is not exported, making it inherently safer for internal-only communication.

Step 3: Analyze the Receiver’s onReceive() Method

Once you’ve identified potential targets from the manifest, the next step is to examine the source code of the receiver class. The entry point for any Broadcast Receiver is the onReceive(Context context, Intent intent) method. This is where the application processes incoming broadcasts.

Focus your analysis on:

  • Intent Actions: Check which actions are handled by the receiver using intent.getAction(). Does it match an action declared in the manifest’s intent filter?
  • Intent Extras: Examine how data passed via intent.getExtras() or specific intent.getStringExtra(), getIntExtra(), etc., is used. This is a prime location for injection vulnerabilities if input is not validated.
  • Permission Checks: Look for explicit calls to context.checkCallingOrSelfPermission() or context.enforceCallingOrSelfPermission(). Even if a permission is declared in the manifest, the code should ideally perform runtime checks, especially for sensitive operations.
  • Sensitive Operations: Identify any methods invoked that perform privileged operations (e.g., file I/O, database access, network requests, reflection) based on incoming intent data.

Example of a vulnerable onReceive method (Java-like pseudocode):

public class VulnerableReceiver extends BroadcastReceiver {    @Override    public void onReceive(Context context, Intent intent) {        if (intent.getAction().equals("com.example.ACTION_VULNERABLE_COMMAND")) {            String command = intent.getStringExtra("command");            if (command != null) {                // WARNING: No permission check and direct execution of unvalidated input!                // This could be a shell command, file path, or database query.                executeCommand(command); // Potentially dangerous operation                Log.d("VulnerableApp", "Executing command: " + command);            }        }    }}

And in Smali (simplified snippet showing how `getStringExtra` might look):

.method public onReceive(Landroid/content/Context;Landroid/content/Intent;)V    .locals 2    iget-object v0, p2, Landroid/content/Intent;->mAction:Ljava/lang/String;    const-string v1, "com.example.ACTION_VULNERABLE_COMMAND"    invoke-virtual {v1, v0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z    move-result v0    if-eqz v0, :cond_0    const-string v0, "command"    invoke-virtual {p2, v0}, Landroid/content/Intent;->getStringExtra(Ljava/lang/String;)Ljava/lang/String;    move-result-object v0    if-eqz v0, :cond_0    # ... calls to a method that uses 'v0' (our command string) without validation    #    invoke-static {v0}, Lcom/example/VulnerableReceiver;->executeCommand(Ljava/lang/String;)V    :cond_0    return-void.end method

Common Broadcast Receiver Vulnerabilities

Lack of Permission Enforcement

Even if an android:permission attribute is declared in the manifest, the application must still enforce this permission at runtime. An exported receiver requiring a permission might still be vulnerable if the onReceive() method doesn’t explicitly check for the sender’s permission before executing sensitive code. Attackers might exploit race conditions or permission bypasses if not properly handled.

Processing Untrusted Input (Intent Extras)

This is arguably the most common and dangerous vulnerability. If a receiver extracts data from Intent extras (e.g., strings, integers, URIs) and uses it directly in sensitive operations without proper validation, an attacker can craft malicious input to achieve various goals:

  • Path Traversal: If intent extra is used as a file path.
  • SQL Injection: If intent extra is used in a database query.
  • Command Injection: If intent extra is passed to a shell command executor.
  • Arbitrary Component Launch: If intent extra is used to construct another Intent for launching a component (Activity, Service) in an unprivileged context.

Always assume intent extras come from an untrusted source, even if the broadcast itself is permission-protected, as the permission might be too broad or poorly designed.

Exported Receivers and Implicit Intents

A receiver declared with android:exported="true" (or implicitly exported due to an <intent-filter>) can be invoked by any app on the system. If such a receiver also responds to implicit intents (i.e., intents that do not specify the target component’s package or class name, relying solely on the action and category), it creates a wide attack surface. Attackers can simply craft an intent matching the action and send it system-wide.

Denial of Service (DoS)

The onReceive() method executes on the application’s main thread. If it performs long-running operations (e.g., complex calculations, network requests, heavy file I/O), it can lead to an Application Not Responding (ANR) error, effectively creating a DoS condition for the target application. Attackers can intentionally send intents that trigger such heavy operations.

Practical Exploitation Example (Conceptual)

Consider the VulnerableReceiver discussed earlier. If it’s exported and lacks a strong permission or an explicit permission check, an attacker can craft a malicious intent using the adb shell am broadcast command:

# Assume target app's package is com.example.vulnerableapp# Assume action is com.example.ACTION_VULNERABLE_COMMAND# Assume receiver takes a 'command' extra that gets executed# This command attempts to delete a sensitive fileadb shell am broadcast 
    -a "com.example.ACTION_VULNERABLE_COMMAND" 
    -n "com.example.vulnerableapp/.VulnerableReceiver" 
    --es "command" "rm /data/data/com.example.vulnerableapp/files/sensitive_data.db"

In this example, the attacker explicitly targets the vulnerable receiver (using -n for component name) and passes a shell command via the

Android Mobile Specs & Compare Directory

Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!

Compare Devices Specs →
Google AdSense Inline Placement - Content Footer banner