Skip to content

MASTG-DEMO-0138: Leaking Sensitive Information via Implicit Intents

Download MASTG-DEMO-0138 APK Open MASTG-DEMO-0138 Folder Build MASTG-DEMO-0138 APK

Sample

This demo uses the same sample app as Internal Activity Communication via Implicit Intent. The app sends user_id and session_token (which are considered sensitive information) as extras in an implicit intent.

Because the intent is implicit, any application that registers an activity with an <intent-filter> that matches the action can receive it and extract those values.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package org.owasp.mastestapp

import android.content.Context
import android.content.Intent

// SUMMARY: This sample demonstrates the insecure use of implicit intents for internal communication.
class MastgTest (private val context: Context){

    fun mastgTest(): String {
        val r = DemoResults("0x01")

        // FAIL: [MASTG-TEST-0372] The app uses an implicit intent to start an internal activity.
        val implicitIntent = Intent().apply {
            action = "org.owasp.mastestapp.INTERNAL_ACTION"
            putExtra("user_id", "12345")
            putExtra("session_token", "abcde-fghij-12345")
            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        }

        try {
            context.startActivity(implicitIntent)
            r.add(Status.FAIL, "Launched internal activity via implicit intent")
        } catch (e: Exception) {
            r.add(Status.ERROR, e.toString())
        }

        /*
        // PASS: [MASTG-TEST-0372] The app uses an explicit intent for internal communication.
        val explicitIntent = Intent(context, InternalActivity::class.java).apply {
            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        }

        try {
            context.startActivity(explicitIntent)
            r.add(Status.PASS, "Launched internal activity via explicit intent")
        } catch (e: Exception) {
            r.add(Status.ERROR, e.toString())
        }
        */

        return r.toJson()
    }
}

class InternalActivity : android.app.Activity() {
    override fun onCreate(savedInstanceState: android.os.Bundle?) {
        super.onCreate(savedInstanceState)
        val textView = android.widget.TextView(this)
        textView.text = "Internal Activity"
        textView.textSize = 24f
        textView.gravity = android.view.Gravity.CENTER
        setContentView(textView)
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package org.owasp.mastestapp;

import android.content.Context;
import android.content.Intent;
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;

/* compiled from: MastgTest.kt */
@Metadata(m110d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0003\n\u0002\u0010\u000e\n\u0000\b\u0007\u0018\u00002\u00020\u0001B\u000f\u0012\u0006\u0010\u0002\u001a\u00020\u0003¢\u0006\u0004\b\u0004\u0010\u0005J\u0006\u0010\u0006\u001a\u00020\u0007R\u000e\u0010\u0002\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000¨\u0006\b"}, m111d2 = {"Lorg/owasp/mastestapp/MastgTest;", "", "context", "Landroid/content/Context;", "<init>", "(Landroid/content/Context;)V", "mastgTest", "", "app_debug"}, m112k = 1, m113mv = {2, 0, 0}, m115xi = 48)
/* loaded from: classes4.dex */
public final class MastgTest {
    public static final int $stable = 8;
    private final Context context;

    public MastgTest(Context context) {
        Intrinsics.checkNotNullParameter(context, "context");
        this.context = context;
    }

    public final String mastgTest() {
        DemoResults r = new DemoResults("0x01");
        Intent implicitIntent = new Intent();
        implicitIntent.setAction("org.owasp.mastestapp.INTERNAL_ACTION");
        implicitIntent.putExtra("user_id", "12345");
        implicitIntent.putExtra("session_token", "abcde-fghij-12345");
        implicitIntent.addFlags(268435456);
        try {
            this.context.startActivity(implicitIntent);
            r.add(Status.FAIL, "Launched internal activity via implicit intent");
        } catch (Exception e) {
            r.add(Status.ERROR, e.toString());
        }
        return r.toJson();
    }
}

Steps

Let's use Static Analysis on Android with an semgrep rule to scan the reverse-engineered code for implicit intents that carry sensitive extras.

../../../../rules/mastg-android-implicit-intent-leaking-extras.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
rules:
  - id: mastg-android-implicit-intent-leaking-extras
    patterns:
      - pattern: |
          $I = new Intent(...);
          ...
          $I.putExtra($KEY, $VAL);
          ...
          $CTX.startActivity($I);
      - pattern-not: |
          $I = new Intent($C, $CLASS);
          ...
      - pattern-not: |
          $I = new Intent(...);
          ...
          $I.setPackage(...);
          ...
          $CTX.startActivity($I);
      - pattern-not: |
          $I = new Intent(...);
          ...
          $I.setComponent(...);
          ...
          $CTX.startActivity($I);
    message: "[MASVS-CODE-4] The app sends an implicit intent with extras. Any app that registers for this action can receive the data. Use explicit intents for internal communication."
    languages: [java]
    severity: WARNING
    metadata:
      summary: Detects implicit intents that carry extras without specifying a target package or component.
run.sh
1
2
3
#!/bin/bash
# SUMMARY: This script uses semgrep to detect implicit intents that leak sensitive data via extras.
NO_COLOR=true semgrep --config ../../../../rules/mastg-android-implicit-intent-leaking-extras.yml ../MASTG-DEMO-0136/MastgTest_reversed.java --text > output.txt

Observation

The output shows one Intent dispatch with extras:

  • Intent creation: new Intent().
  • Action assignment: implicitIntent.setAction("org.owasp.mastestapp.INTERNAL_ACTION").
  • Extra keys and values: user_id with value 12345 and session_token with value abcde-fghij-12345.
  • Dispatch API: this.context.startActivity(implicitIntent).
output.txt
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
┌────────────────┐
 1 Code Finding 
└────────────────┘

    ../MASTG-DEMO-0136/MastgTest_reversed.java
    ❯❱ rules.mastg-android-implicit-intent-leaking-extras
          [MASVS-CODE-4] The app sends an implicit intent with extras. Any app that registers for this action
          can receive the data. Use explicit intents for internal communication.                             

           22 Intent implicitIntent = new Intent();
           23 implicitIntent.setAction("org.owasp.mastestapp.INTERNAL_ACTION");
           24 implicitIntent.putExtra("user_id", "12345");
           25 implicitIntent.putExtra("session_token", "abcde-fghij-12345");
           26 implicitIntent.addFlags(268435456);
           27 try {
           28     this.context.startActivity(implicitIntent);
           29     r.add(Status.FAIL, "Launched internal activity via implicit intent");
           30 } catch (Exception e) {
           31     r.add(Status.ERROR, e.toString());
             [hid 1 additional lines, adjust with --max-lines-per-finding] 

Evaluation

The test case fails because the app attaches sensitive extras (user_id and session_token) to an implicit intent and dispatches it without constraining the recipient.

The reported dispatch does not name a target package or component and does not verify the selected handler's identity before dispatch. The reported block does not show a target-defining call such as setPackage, setClass, setClassName, setComponent, or an explicit Intent(context, Class) constructor. Any app that declares a matching <intent-filter> for org.owasp.mastestapp.INTERNAL_ACTION can become a candidate and receive the full extras Bundle.