Privilege Escalation Vulnerability
ID |
java.android_privilege_escalation_attack |
Severity |
critical |
Resource |
Access Control |
Language |
Java |
Tags |
CWE:250, CWE:269, MASVS:MSTG-PLATFORM-4, OWASP:2021:A1, android, privilege-escalation |
Description
Android privilege escalation occurs when applications gain unauthorized access to resources, permissions, or capabilities beyond what was explicitly granted. This bypasses Android’s security sandbox model, which assigns different user IDs (UIDs) to each app for privilege separation.
Rationale
Privilege escalation vulnerability enables attackers to:
-
Bypass runtime permission checks
-
Execute operations requiring dangerous permissions without user consent
-
Gain access to protected data (contacts, SMS, photos, files)
-
Hijack application components through PendingIntent manipulation
Applications may unintentionally:
-
Grant URI permissions to untrusted apps
-
Allow PendingIntent hijacking for privilege gain
-
Break app sandboxing through shared user IDs
This vulnerability is related to Improper Privilege Management (CWE-269) and Execution with Unnecessary Privileges (CWE-250).
This detector identifies privilege escalation across multiple attack vectors:
-
PendingIntentwithFLAG_MUTABLE:
Implicit Intent + FLAG_MUTABLE allows PendingIntent hijacking:
// VULNERABLE: Implicit Intent with FLAG_MUTABLE
val intent = Intent("com.example.ACTION") // No component set
val pendingIntent = PendingIntent.getActivity(// FLAW
context, 0, intent,
PendingIntent.FLAG_MUTABLE
)
// Used in notification
notificationManager.notify(1, notification)
A malicious app can then intercept and modify the PendingIntent:
// Attacker fills in missing component to access private data
notification.contentIntent.send(
context, 0,
Intent().apply {
setClassName("com.victim.app", "com.victim.PrivateActivity")
}
)
-
setResultwithgetIntent():
Returning incoming Intent via setResult() grants URI permissions:
// VULNERABLE: Reuses incoming Intent
class ResultActivity : AppCompatActivity() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
setResult(RESULT_OK, intent) // getIntent() reused// FLAW
finish()
}
}
Combined with ContentProvider:
<provider
android:name=".ContactsProvider"
android:grantUriPermissions="true" /><!-- FLAW -->
An attacker then can gain access to ContentProvider data:
val attack = Intent().apply {
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
startActivityForResult(attack, REQUEST_CODE)
// Result contains URI permission to contacts
-
Shared User ID
Breaking app sandboxing through sharedUserId is a recipe to disaster:
<!-- VULNERABLE: Shares UID with other apps -->
<manifest
package="com.example.app"
android:sharedUserId="com.example.shared"><!-- FLAW -->
</manifest>
Apps with same sharedUserId can access each other’s data and run in same process.
Remediation
-
Use FLAG_IMMUTABLE for PendingIntent
Always use explicit Intents with FLAG_IMMUTABLE:
// SECURE: Explicit Intent + FLAG_IMMUTABLE val intent = Intent(context, TargetActivity::class.java) intent.setPackage(packageName) val pendingIntent = PendingIntent.getActivity( context, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_ONE_SHOT ) -
Create New Intent for setResult
Never reuse incoming Intent:
// SECURE: Create new Intent with only safe data override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { val result = Intent() result.putExtra("safe_data", sanitizedValue) setResult(RESULT_OK, result) finish() } -
Limit grantUriPermissions
Use path-specific grants instead of blanket permission:
<!-- SECURE: Limit to specific paths --> <provider android:name=".ContentProvider" android:grantUriPermissions="false"> <grant-uri-permission android:pathPrefix="/public/" /> </provider> -
Remove sharedUserId
Migrate to alternative approaches:
<!-- DON'T: Shared UID breaks sandboxing --> <manifest android:sharedUserId="com.example.shared" /> <!-- DO: Use ContentProvider with signature permission --> <provider android:name=".SharedDataProvider" android:permission="com.example.SIGNATURE_PERM" /> <permission android:name="com.example.SIGNATURE_PERM" android:protectionLevel="signature" />
References
-
Android Developer: PendingIntent Security Best Practices
-
OWASP MASTG-TEST-0027: Testing App Permissions
-
CWE-269: Improper Privilege Management
-
CWE-250: Execution with Unnecessary Privileges
-
OWASP Top 10 2021 A01: Broken Access Control