This sample demonstrates insecure uses of PendingIntent in Android, including mutable PendingIntents and implicit base intents that could be vulnerable to hijacking by malicious applications.
The code shows four different scenarios:
Mutable PendingIntent with implicit intent: Creates a PendingIntent using FLAG_UPDATE_CURRENT without FLAG_IMMUTABLE, combined with an implicit intent (only specifying ACTION_VIEW). This is the most dangerous combination as it allows a malicious app to intercept and modify the intent.
Explicit FLAG_MUTABLE: Creates a PendingIntent with FLAG_MUTABLE explicitly set. While the base intent is explicit (targeting a specific class), the mutable flag allows modification of intent fields.
Broadcast with implicit intent and no flags: Creates a broadcast PendingIntent with an implicit intent (custom action string) and no flags. On API levels below 31, this defaults to mutable.
Secure PendingIntent (PASS): Creates a PendingIntent with FLAG_IMMUTABLE and an explicit intent that specifies both the target class and package. This is the recommended secure approach.
packageorg.owasp.mastestappimportandroid.app.PendingIntentimportandroid.content.Contextimportandroid.content.Intentimportandroid.os.BuildclassMastgTest(privatevalcontext:Context){// SUMMARY: This sample demonstrates insecure uses of PendingIntent in Android, including mutable PendingIntents and implicit base intents.funmastgTest():String{valresults=StringBuilder()// FAIL: [MASTG-TEST-0313] Mutable PendingIntent with implicit intent - vulnerable to hijackingvalimplicitIntent=Intent(Intent.ACTION_VIEW)valmutablePendingIntent=PendingIntent.getActivity(context,0,implicitIntent,PendingIntent.FLAG_UPDATE_CURRENT// Missing FLAG_IMMUTABLE)results.append("Created mutable PendingIntent with implicit intent\n")// FAIL: [MASTG-TEST-0313] Explicit FLAG_MUTABLE used without justificationvalexplicitMutableIntent=Intent(context,MastgTest::class.java)valexplicitMutablePendingIntent=PendingIntent.getService(context,1,explicitMutableIntent,PendingIntent.FLAG_MUTABLE)results.append("Created explicitly mutable PendingIntent\n")// FAIL: [MASTG-TEST-0313] Broadcast with implicit intentvalbroadcastIntent=Intent("com.example.CUSTOM_ACTION")valbroadcastPendingIntent=PendingIntent.getBroadcast(context,2,broadcastIntent,0// No flags - mutable by default on API < 31)results.append("Created broadcast PendingIntent with implicit intent\n")// PASS: [MASTG-TEST-0313] Secure PendingIntent with FLAG_IMMUTABLE and explicit intentvalsecureIntent=Intent(context,MastgTest::class.java).apply{setPackage(context.packageName)}valsecurePendingIntent=PendingIntent.getActivity(context,3,secureIntent,PendingIntent.FLAG_IMMUTABLE)results.append("Created secure PendingIntent with FLAG_IMMUTABLE\n")returnresults.toString()}}
rules:-id:mastg-android-pendingintent-mutableseverity:WARNINGlanguages:-javametadata:summary:DetectsPendingIntentcreationthatmaybevulnerableduetomutability.message:"[MASVS-PLATFORM-1] PendingIntent created without FLAG_IMMUTABLE or with FLAG_MUTABLE may be vulnerable to hijacking."pattern-either:-patterns:-pattern:PendingIntent.getActivity($CTX,$REQ,$INTENT,$FLAGS)-metavariable-regex:metavariable:$FLAGSregex:^(0|134217728|33554432|0x08000000|0x02000000|PendingIntent\.FLAG_UPDATE_CURRENT|PendingIntent\.FLAG_MUTABLE)$-patterns:-pattern:PendingIntent.getActivities($CTX,$REQ,$INTENTS,$FLAGS)-metavariable-regex:metavariable:$FLAGSregex:^(0|134217728|33554432|0x08000000|0x02000000|PendingIntent\.FLAG_UPDATE_CURRENT|PendingIntent\.FLAG_MUTABLE)$-patterns:-pattern:PendingIntent.getActivities($CTX,$REQ,$INTENTS,$FLAGS,$BUNDLE)-metavariable-regex:metavariable:$FLAGSregex:^(0|134217728|33554432|0x08000000|0x02000000|PendingIntent\.FLAG_UPDATE_CURRENT|PendingIntent\.FLAG_MUTABLE)$-patterns:-pattern:PendingIntent.getService($CTX,$REQ,$INTENT,$FLAGS)-metavariable-regex:metavariable:$FLAGSregex:^(0|134217728|33554432|0x08000000|0x02000000|PendingIntent\.FLAG_UPDATE_CURRENT|PendingIntent\.FLAG_MUTABLE)$-patterns:-pattern:PendingIntent.getForegroundService($CTX,$REQ,$INTENT,$FLAGS)-metavariable-regex:metavariable:$FLAGSregex:^(0|134217728|33554432|0x08000000|0x02000000|PendingIntent\.FLAG_UPDATE_CURRENT|PendingIntent\.FLAG_MUTABLE)$-patterns:-pattern:PendingIntent.getBroadcast($CTX,$REQ,$INTENT,$FLAGS)-metavariable-regex:metavariable:$FLAGSregex:^(0|134217728|33554432|0x08000000|0x02000000|PendingIntent\.FLAG_UPDATE_CURRENT|PendingIntent\.FLAG_MUTABLE)$
Line 25: FAIL - Uses PendingIntent.getActivity() with an implicit intent (ACTION_VIEW) and FLAG_UPDATE_CURRENT without FLAG_IMMUTABLE. A malicious app could intercept this PendingIntent and modify its target.
Line 28: FAIL - Uses PendingIntent.getService() with FLAG_MUTABLE explicitly set. Even though the intent is explicit, the mutable flag allows modification of unfilled fields.
Line 31: FAIL - Uses PendingIntent.getBroadcast() with an implicit intent (custom action) and no flags. On API < 31, the absence of flags results in implicit mutability.
The test fails for the three instances because they either lack FLAG_IMMUTABLE or use implicit intents that could be hijacked.
The sample creates PendingIntent instances using mutable or non-immutable flags. If exposed to external applications, they may become exploitable when shared through notifications, widgets, shortcuts, or IPC mechanisms.
For the getActivity() instance, the base intent uses Intent.ACTION_VIEW without specifying a target component. If the PendingIntent is exposed, an attacker-controlled application capable of handling the same action could be selected as the destination when the intent is resolved.
For the getService() instance, FLAG_MUTABLE is explicitly specified. If an attacker obtains a reference to the PendingIntent, mutable intent fields such as extras, actions, or data URIs may be modified before the intent is delivered to the target service.
For the getBroadcast() instance, the broadcast uses an implicit action and does not specify FLAG_IMMUTABLE. An attacker-controlled application could register a receiver for the same action and potentially receive or influence the broadcast when the PendingIntent is sent.