-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Port MASTG-TEST-0026: Testing Implicit Intents (android) (by @appknox) #3271
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
ea2f924
3ab2c2d
6a6b447
bf6baa4
afc7e8c
c4092ce
f9191bd
fafeb66
2c7ee5b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
| package="com.example.datainteceptor"> | ||
|
|
||
| <application android:label="AttackerApp"> | ||
|
|
||
| <!-- Default launcher activity --> | ||
| <activity android:name=".MainActivity" | ||
| android:exported="true"> | ||
| <intent-filter> | ||
| <action android:name="android.intent.action.MAIN" /> | ||
| <category android:name="android.intent.category.LAUNCHER" /> | ||
| </intent-filter> | ||
| </activity> | ||
|
|
||
| <!-- Interceptor activity to catch implicit intent --> | ||
| <activity android:name=".InterceptorActivity" | ||
| android:exported="true"> | ||
| <intent-filter> | ||
| <action android:name="org.owasp.mastestapp.PROCESS_SENSITIVE_DATA" /> | ||
| <category android:name="android.intent.category.DEFAULT" /> | ||
| </intent-filter> | ||
| </activity> | ||
|
|
||
| </application> | ||
| </manifest> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
| android:versionCode="1" | ||
| android:versionName="1.0" | ||
| android:compileSdkVersion="35" | ||
| android:compileSdkVersionCodename="15" | ||
| package="org.owasp.mastestapp" | ||
| platformBuildVersionCode="35" | ||
| platformBuildVersionName="15"> | ||
| <uses-sdk | ||
| android:minSdkVersion="29" | ||
| android:targetSdkVersion="35"/> | ||
| <uses-permission android:name="android.permission.INTERNET"/> | ||
| <permission | ||
| android:name="org.owasp.mastestapp.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION" | ||
| android:protectionLevel="signature"/> | ||
| <uses-permission android:name="org.owasp.mastestapp.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"/> | ||
| <application | ||
| android:theme="@style/Theme.MASTestApp" | ||
| android:label="@string/app_name" | ||
| android:icon="@mipmap/ic_launcher" | ||
| android:debuggable="true" | ||
| android:testOnly="true" | ||
| android:allowBackup="true" | ||
| android:supportsRtl="true" | ||
| android:extractNativeLibs="false" | ||
| android:fullBackupContent="@xml/backup_rules" | ||
| android:roundIcon="@mipmap/ic_launcher_round" | ||
| android:appComponentFactory="androidx.core.app.CoreComponentFactory" | ||
| android:dataExtractionRules="@xml/data_extraction_rules"> | ||
| <activity | ||
| android:theme="@style/Theme.MASTestApp" | ||
| android:name="org.owasp.mastestapp.MainActivity" | ||
| android:exported="true" | ||
| android:windowSoftInputMode="adjustResize"> | ||
| <intent-filter> | ||
| <action android:name="android.intent.action.MAIN"/> | ||
| <category android:name="android.intent.category.LAUNCHER"/> | ||
| </intent-filter> | ||
| </activity> | ||
| <activity | ||
| android:name="org.owasp.mastestapp.VulnerableActivity" | ||
| android:exported="true"> | ||
| <intent-filter> | ||
| <action android:name="org.owasp.mastestapp.PROCESS_SENSITIVE_DATA"/> | ||
| <category android:name="android.intent.category.DEFAULT"/> | ||
| </intent-filter> | ||
| </activity> | ||
| <activity | ||
| android:name="androidx.compose.ui.tooling.PreviewActivity" | ||
| android:exported="true"/> | ||
| <activity | ||
| android:name="androidx.activity.ComponentActivity" | ||
| android:exported="true"/> | ||
| <provider | ||
| android:name="androidx.startup.InitializationProvider" | ||
| android:exported="false" | ||
| android:authorities="org.owasp.mastestapp.androidx-startup"> | ||
| <meta-data | ||
| android:name="androidx.emoji2.text.EmojiCompatInitializer" | ||
| android:value="androidx.startup"/> | ||
| <meta-data | ||
| android:name="androidx.lifecycle.ProcessLifecycleInitializer" | ||
| android:value="androidx.startup"/> | ||
| <meta-data | ||
| android:name="androidx.profileinstaller.ProfileInstallerInitializer" | ||
| android:value="androidx.startup"/> | ||
| </provider> | ||
| <receiver | ||
| android:name="androidx.profileinstaller.ProfileInstallReceiver" | ||
| android:permission="android.permission.DUMP" | ||
| android:enabled="true" | ||
| android:exported="true" | ||
| android:directBootAware="false"> | ||
| <intent-filter> | ||
| <action android:name="androidx.profileinstaller.action.INSTALL_PROFILE"/> | ||
| </intent-filter> | ||
| <intent-filter> | ||
| <action android:name="androidx.profileinstaller.action.SKIP_FILE"/> | ||
| </intent-filter> | ||
| <intent-filter> | ||
| <action android:name="androidx.profileinstaller.action.SAVE_PROFILE"/> | ||
| </intent-filter> | ||
| <intent-filter> | ||
| <action android:name="androidx.profileinstaller.action.BENCHMARK_OPERATION"/> | ||
| </intent-filter> | ||
| </receiver> | ||
| </application> | ||
| </manifest> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| --- | ||
| platform: android | ||
| title: Implicit Intent Hijacking with same custom intent filter | ||
| id: MASTG-DEMO-0058 | ||
| code: [kotlin] | ||
| test: MASTG-TEST-0286 | ||
| profiles: [L1, L2] | ||
| --- | ||
|
|
||
| ### Sample | ||
|
|
||
| The code snippet below demonstrates the use of an implicit intent with sensitive data and a custom action, without specifying a target component. | ||
|
|
||
| {{ MastgTest.kt # MastgTest_reversed.java }} | ||
|
|
||
| ### MastgTestAttacker.kt | ||
|
|
||
| The attacker app has an exported activity that includes a corresponding `<intent-filter>` which registers the identical custom action, enabling it to capture the implicit intent sent out by the victim app. | ||
|
|
||
| {{ MastgTestAttacker.kt }} | ||
|
|
||
| ### MastgTestInternalData.kt | ||
|
|
||
| A component within the vulnerable app originally intended to process sensitive data, but is exposed via an implicit intent mechanism. Although not explicitly targeted, it can be hijacked by any app that claims to handle the same action. | ||
|
|
||
| {{ MastgTestInternalData.kt }} | ||
|
|
||
| ### Steps | ||
|
|
||
| 1. Install the attacker app on a device using @MASTG-TECH-0004. | ||
| 2. On the vulnerable app, click on start to start the test. | ||
|
|
||
| {{ MastgTestAttacker.kt }} | ||
| {{ AndroidManifestAttacker_app.xml }} | ||
|
|
||
| ### Observation | ||
|
|
||
| The attacker app successfully intercepted the intent containing sensitive extras such as tokens, API keys, and credentials. This confirms that any app declaring a matching `<intent-filter>` can receive these values without restriction. | ||
|
|
||
| ### Evaluation | ||
|
|
||
| The test fails due to the use of an exported activity (VulnerableActivity) that includes an intent filter with a custom action. Combined with the implicit intent in `MastgTest.kt`, this creates a vulnerable pattern where sensitive data is transmitted to an untrusted receiver. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| package org.owasp.mastestapp | ||
|
|
||
| import android.util.Log | ||
| import android.content.Context | ||
| import android.content.Intent | ||
|
|
||
| class MastgTest (private val context: Context){ | ||
|
|
||
| fun mastgTest(): String { | ||
| val sensitiveString = "Hello from the OWASP MASTG Test app." | ||
|
|
||
| // Vulnerable: Using implicit intent with sensitive data | ||
| val vulnerableIntent = Intent().apply { | ||
| action = "org.owasp.mastestapp.PROCESS_SENSITIVE_DATA" | ||
| putExtra("sensitive_token", "auth_token_12345") | ||
| putExtra("user_credentials", "admin:password123") | ||
| putExtra("api_key", "sk-1234567890abcdef") | ||
| putExtra("message", sensitiveString) | ||
| } | ||
|
|
||
| // Launch implicit intent - any app can intercept this | ||
| try { | ||
| context.startActivity(vulnerableIntent) | ||
| Log.d("MASTG-TEST", "Launched vulnerable implicit intent with sensitive data") | ||
| } catch (e: Exception) { | ||
| Log.e("MASTG-TEST", "Failed to launch intent: ${e.message}") | ||
| } | ||
|
|
||
| Log.d("MASTG-TEST", sensitiveString) | ||
| return sensitiveString | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,24 @@ | ||||||
| package com.example.datainteceptor | ||||||
|
||||||
| package com.example.datainteceptor | |
| package com.example.datainterceptor |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,15 @@ | ||||||
| package com.example.datainteceptor | ||||||
|
||||||
| package com.example.datainteceptor | |
| package com.example.datainterceptor |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| package org.owasp.mastestapp | ||
|
|
||
| import android.app.Activity | ||
| import android.os.Bundle | ||
| import android.util.Log | ||
| import android.widget.TextView | ||
|
|
||
| class VulnerableActivity : Activity() { | ||
|
|
||
| override fun onCreate(savedInstanceState: Bundle?) { | ||
| super.onCreate(savedInstanceState) | ||
|
|
||
| // Simple layout to show the received data | ||
| val textView = TextView(this) | ||
| textView.text = "Processing sensitive data..." | ||
| setContentView(textView) | ||
|
|
||
| // Process the received intent data | ||
| val receivedData = StringBuilder("Received sensitive data:\n") | ||
|
|
||
| intent?.let { intent -> | ||
| intent.getStringExtra("sensitive_token")?.let { | ||
| receivedData.append("Token: $it\n") | ||
| Log.d("VULNERABLE-APP", "Received token: $it") | ||
| } | ||
|
|
||
| intent.getStringExtra("user_credentials")?.let { | ||
| receivedData.append("Credentials: $it\n") | ||
| Log.d("VULNERABLE-APP", "Received credentials: $it") | ||
| } | ||
|
|
||
| intent.getStringExtra("api_key")?.let { | ||
| receivedData.append("API Key: $it\n") | ||
| Log.d("VULNERABLE-APP", "Received API key: $it") | ||
| } | ||
|
|
||
| intent.getStringExtra("message")?.let { | ||
| receivedData.append("Message: $it\n") | ||
| Log.d("VULNERABLE-APP", "Received message: $it") | ||
| } | ||
| } | ||
|
|
||
| textView.text = receivedData.toString() | ||
| Log.d("VULNERABLE-APP", "VulnerableActivity processed data") | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package org.owasp.mastestapp; | ||
|
|
||
| import android.content.Context; | ||
| import android.content.Intent; | ||
| import android.util.Log; | ||
| import kotlin.Metadata; | ||
| import kotlin.jvm.internal.Intrinsics; | ||
|
|
||
| /* compiled from: MastgTest.kt */ | ||
| @Metadata(d1 = {"\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"}, d2 = {"Lorg/owasp/mastestapp/MastgTest;", "", "context", "Landroid/content/Context;", "<init>", "(Landroid/content/Context;)V", "mastgTest", "", "app_debug"}, k = 1, mv = {2, 0, 0}, xi = 48) | ||
| /* loaded from: classes3.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() { | ||
| Intent vulnerableIntent = new Intent(); | ||
| vulnerableIntent.setAction("org.owasp.mastestapp.PROCESS_SENSITIVE_DATA"); | ||
| vulnerableIntent.putExtra("sensitive_token", "auth_token_12345"); | ||
| vulnerableIntent.putExtra("user_credentials", "admin:password123"); | ||
| vulnerableIntent.putExtra("api_key", "sk-1234567890abcdef"); | ||
| vulnerableIntent.putExtra("message", "Hello from the OWASP MASTG Test app."); | ||
| try { | ||
| this.context.startActivity(vulnerableIntent); | ||
| Log.d("MASTG-TEST", "Launched vulnerable implicit intent with sensitive data"); | ||
| } catch (Exception e) { | ||
| Log.e("MASTG-TEST", "Failed to launch intent: " + e.getMessage()); | ||
| } | ||
| Log.d("MASTG-TEST", "Hello from the OWASP MASTG Test app."); | ||
| return "Hello from the OWASP MASTG Test app."; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,36 @@ | ||||||
| --- | ||||||
| platform: android | ||||||
| title: Implicit intent to trigger internal app components | ||||||
| id: MASTG-DEMO-0059 | ||||||
| code: [kotlin] | ||||||
| test: MASTG-TEST-0287 | ||||||
| profiles: [L1, L2] | ||||||
| --- | ||||||
|
|
||||||
| ### Sample | ||||||
|
|
||||||
| The manifest snippet outlines an exported activity featuring an `<intent-filter>` with a unique action. This allows the component to be reachable by any application on the device that registers the identical intent action, which could allow a malicious app to capture such intents. | ||||||
|
|
||||||
| {{ ../MASTG-DEMO-0058/AndroidManifest_reversed.xml }} | ||||||
|
|
||||||
| ### Steps | ||||||
|
|
||||||
| Let's run our @MASTG-TOOL-0110 rule against the manifest file and code. | ||||||
|
|
||||||
| {{ ../../../../rules/mastg-android-custom-intent-filter-intercept.yml }} | ||||||
|
||||||
| {{ ../../../../rules/mastg-android-custom-intent-filter-intercept.yml }} | |
| {{ ../../../../rules/mastg-android-custom-intent-intecept.yml }} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
|
|
||
|
|
||
| ┌────────────────┐ | ||
| │ 1 Code Finding │ | ||
| └────────────────┘ | ||
|
|
||
| AndroidManifest_reversed.xml | ||
| ❯❱ exported-implicit-custom-action | ||
| Exported activity with custom implicit intent action. May enable intent hijacking. | ||
|
|
||
| 41┆ <activity | ||
| 42┆ android:name="org.owasp.mastestapp.VulnerableActivity" | ||
| 43┆ android:exported="true"> | ||
| 44┆ <intent-filter> | ||
| 45┆ <action android:name="org.owasp.mastestapp.PROCESS_SENSITIVE_DATA"/> | ||
| 46┆ <category android:name="android.intent.category.DEFAULT"/> | ||
| 47┆ </intent-filter> | ||
| 48┆ </activity> |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,2 @@ | ||||||
| # shellcheck disable=SC2148 | ||||||
| NO_COLOR=true semgrep -c ../../../../rules/mastg-android-custom-intent-filter-intercept.yml ../MASTG-DEMO-0058/AndroidManifest_reversed.xml --text -o output.txt | ||||||
|
||||||
| NO_COLOR=true semgrep -c ../../../../rules/mastg-android-custom-intent-filter-intercept.yml ../MASTG-DEMO-0058/AndroidManifest_reversed.xml --text -o output.txt | |
| NO_COLOR=true semgrep -c ../../../../rules/mastg-android-custom-intent-intecept.yml ../MASTG-DEMO-0058/AndroidManifest_reversed.xml --text -o output.txt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a typo in the package name: 'datainteceptor' should be 'datainterceptor' to match the correct spelling of 'interceptor'.