-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Port MASTG-TEST-0028: Testing for Testing Deep Links (android) (by @appknox) #3463
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 6 commits
7b32ee2
516166a
bdb97d8
afbe8b6
aa1894b
a14745d
d95df4f
05ad8a3
b4efe36
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,86 @@ | ||
| <?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:allowBackup="true" | ||
| android:supportsRtl="true" | ||
| android:extractNativeLibs="false" | ||
| android:usesCleartextTraffic="true" | ||
| android:roundIcon="@mipmap/ic_launcher_round" | ||
| android:appComponentFactory="androidx.core.app.CoreComponentFactory"> | ||
| <activity | ||
| android:theme="@style/Theme.MASTestApp" | ||
| android:name="org.owasp.mastestapp.MainActivity" | ||
| android:exported="true"> | ||
| <intent-filter> | ||
| <action android:name="android.intent.action.VIEW"/> | ||
| <category android:name="android.intent.category.DEFAULT"/> | ||
| <category android:name="android.intent.category.BROWSABLE"/> | ||
| <data | ||
| android:scheme="vulnerable-app" | ||
| android:host="deeplink"/> | ||
| </intent-filter> | ||
| <intent-filter> | ||
| <action android:name="android.intent.action.MAIN"/> | ||
| <category android:name="android.intent.category.LAUNCHER"/> | ||
| </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,32 @@ | ||
| --- | ||
| platform: android | ||
| title: Unvalidated URL from Deep Link Loaded in WebView with semgrep | ||
| id: MASTG-DEMO-XXXA | ||
| code: [kotlin] | ||
| test: MASTG-TEST-XXXA | ||
| status: new | ||
|
Comment on lines
+1
to
+7
|
||
| --- | ||
|
|
||
| ### Sample | ||
cpholguera marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| The following is a sample code file that contains a function to handle a deep link, which insecurely loads a URL into a WebView. | ||
|
|
||
| {{ MastgTest.kt # MastgTest_reversed.java }} | ||
|
|
||
| ### Steps | ||
cpholguera marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Let's run @MASTG-TOOL-0110 rules against the sample code. | ||
|
|
||
| {{ ../../../../rules/mastg-android-unvalidated-deeplink-data.yml }} | ||
|
|
||
| {{ run.sh }} | ||
|
|
||
| ### Observation | ||
cpholguera marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| The output file shows usage of dangerous data flow from a source `getQueryParameter` to a sink `loadUrl`. | ||
|
|
||
| {{ output.txt }} | ||
|
|
||
| ### Evaluation | ||
cpholguera marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| The test fails because the app loads a user-controllable URL from a deep link directly into a WebView without validation. | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,32 @@ | ||||||
| package org.owasp.mastestapp | ||||||
|
|
||||||
| import android.annotation.SuppressLint | ||||||
| import android.content.Context | ||||||
| import android.net.Uri | ||||||
| import android.webkit.WebView | ||||||
| import androidx.activity.ComponentActivity | ||||||
|
|
||||||
| class MastgTest(private val context: Context) { | ||||||
|
|
||||||
| fun mastgTest(): String { | ||||||
| return """ | ||||||
| This app is vulnerable to deep link attacks. | ||||||
|
|
||||||
| Test with: | ||||||
| adb shell am start -a android.intent.action.VIEW -d "vulnerable-app://deeplink?url=https://example.com" | ||||||
| """.trimIndent() | ||||||
| } | ||||||
|
|
||||||
| @SuppressLint("SetJavaScriptEnabled") | ||||||
| fun processDeepLinkAndLoad(uri: Uri?) { | ||||||
| if (uri == null) return | ||||||
|
|
||||||
| val url = uri.getQueryParameter("url") | ||||||
| if (url != null) { | ||||||
| val webView = WebView(context) | ||||||
| webView.settings.javaScriptEnabled = true | ||||||
| webView.loadUrl(url) | ||||||
| (context as ComponentActivity).setContentView(webView) | ||||||
|
||||||
| (context as ComponentActivity).setContentView(webView) | |
| (context as? ComponentActivity)?.setContentView(webView) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package org.owasp.mastestapp; | ||
|
|
||
| import android.content.Context; | ||
| import android.net.Uri; | ||
| import android.webkit.WebView; | ||
| import androidx.activity.ComponentActivity; | ||
| import kotlin.Metadata; | ||
| import kotlin.jvm.internal.Intrinsics; | ||
|
|
||
| /* compiled from: MastgTest.kt */ | ||
| @Metadata(d1 = {"\u0000$\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0003\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\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\u0007J\u0012\u0010\b\u001a\u00020\t2\b\u0010\n\u001a\u0004\u0018\u00010\u000bH\u0007R\u000e\u0010\u0002\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000¨\u0006\f"}, d2 = {"Lorg/owasp/mastestapp/MastgTest;", "", "context", "Landroid/content/Context;", "<init>", "(Landroid/content/Context;)V", "mastgTest", "", "processDeepLinkAndLoad", "", "uri", "Landroid/net/Uri;", "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() { | ||
| return "This app is vulnerable to deep link attacks.\n\nTest with:\nadb shell am start -a android.intent.action.VIEW -d \"vulnerable-app://deeplink?url=https://example.com\""; | ||
| } | ||
|
|
||
| public final void processDeepLinkAndLoad(Uri uri) { | ||
| String url; | ||
| if (uri != null && (url = uri.getQueryParameter("url")) != null) { | ||
| WebView webView = new WebView(this.context); | ||
| webView.getSettings().setJavaScriptEnabled(true); | ||
| webView.loadUrl(url); | ||
| Context context = this.context; | ||
| Intrinsics.checkNotNull(context, "null cannot be cast to non-null type androidx.activity.ComponentActivity"); | ||
| ((ComponentActivity) context).setContentView(webView); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
|
|
||
|
|
||
|
|
||
| ┌────────────────┐ | ||
| │ 1 Code Finding │ | ||
| └────────────────┘ | ||
|
|
||
| MastgTest_reversed.java | ||
| ❯❱ android-unvalidated-deeplink-data | ||
| [MASVS-PLATFORM] Unvalidated deep link query parameters are directly loaded into a WebView. | ||
|
|
||
| 31┆ webView.loadUrl(url); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| NO_COLOR=true semgrep -c ../../../../rules/mastg-android-unvalidated-deeplink-data.yml ./MastgTest_reversed.java --text -o output.txt |
cpholguera marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,86 @@ | ||||||||||
| <?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:allowBackup="true" | ||||||||||
| android:supportsRtl="true" | ||||||||||
| android:extractNativeLibs="false" | ||||||||||
| android:usesCleartextTraffic="true" | ||||||||||
| android:roundIcon="@mipmap/ic_launcher_round" | ||||||||||
| android:appComponentFactory="androidx.core.app.CoreComponentFactory"> | ||||||||||
| <activity | ||||||||||
| android:theme="@style/Theme.MASTestApp" | ||||||||||
| android:name="org.owasp.mastestapp.MainActivity" | ||||||||||
| android:exported="true"> | ||||||||||
| <intent-filter> | ||||||||||
| <action android:name="android.intent.action.VIEW"/> | ||||||||||
| <category android:name="android.intent.category.DEFAULT"/> | ||||||||||
| <category android:name="android.intent.category.BROWSABLE"/> | ||||||||||
| <data | ||||||||||
| android:scheme="vulnerable-app" | ||||||||||
| android:host="deeplink"/> | ||||||||||
|
Comment on lines
+38
to
+39
|
||||||||||
| android:scheme="vulnerable-app" | |
| android:host="deeplink"/> | |
| android:scheme="https" | |
| android:host="vulnerable-app.example"/> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| --- | ||
| platform: android | ||
| title: Deep Link Intent Filter Missing android:autoVerify with semgrep | ||
| id: MASTG-DEMO-XXXB | ||
| code: [kotlin] | ||
| test: MASTG-TEST-XXXB | ||
| status: new | ||
|
Comment on lines
+1
to
+7
|
||
| --- | ||
|
|
||
| ### Sample | ||
cpholguera marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| The following is a sample `AndroidManifest.xml` snippet that defines a deep link intent filter without the `android:autoVerify="true"` attribute. | ||
|
|
||
| {{ AndroidManifest_reversed.xml }} | ||
|
|
||
| ### Steps | ||
cpholguera marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Let's run @MASTG-TOOL-0110 rules against the sample manifest. | ||
|
|
||
| {{ ../../../../rules/mastg-android-autoverify-missing.yml }} | ||
|
|
||
| {{ run.sh }} | ||
|
|
||
| ### Observation | ||
cpholguera marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| The rule has identified that the deep link intent filter is missing the `android:autoVerify="true"` attribute. | ||
|
|
||
| {{ output.txt }} | ||
|
|
||
| ### Evaluation | ||
cpholguera marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| The test fails because the app does not enforce Android App Links verification. Without `android:autoVerify="true"`, malicious apps may intercept the app's deep links, leading to phishing or hijacking attacks. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
|
|
||
|
|
||
| ┌────────────────┐ | ||
| │ 1 Code Finding │ | ||
| └────────────────┘ | ||
|
|
||
| AndroidManifest_reversed.xml | ||
| ❯❱ android-autoverify-missing | ||
| [MASVS-PLATFORM] Deep link intent filter missing android:autoVerify="true",enabling | ||
| malicious apps to hijack links. | ||
|
|
||
| 33┆ <intent-filter> | ||
| 34┆ <action android:name="android.intent.action.VIEW"/> | ||
| 35┆ <category android:name="android.intent.category.DEFAULT"/> | ||
| 36┆ <category android:name="android.intent.category.BROWSABLE"/> | ||
| 37┆ <data | ||
| 38┆ android:scheme="vulnerable-app" | ||
| 39┆ android:host="deeplink"/> | ||
| 40┆ </intent-filter> |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1 @@ | ||||||
| NO_COLOR=true semgrep -c ../../../../rules/mastg-android-autoverify-missing.yml ../MASTG-DEMO-0062/AndroidManifest_reversed.xml --text -o output.txt | ||||||
|
||||||
| NO_COLOR=true semgrep -c ../../../../rules/mastg-android-autoverify-missing.yml ../MASTG-DEMO-0062/AndroidManifest_reversed.xml --text -o output.txt | |
| NO_COLOR=true semgrep -c ../../../../rules/mastg-android-autoverify-missing.yml ./AndroidManifest_reversed.xml --text -o output.txt |
cpholguera marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
| xmlns:tools="http://schemas.android.com/tools"> | ||
|
|
||
| <uses-permission android:name="android.permission.INTERNET" /> | ||
|
|
||
| <application | ||
| android:allowBackup="true" | ||
| android:dataExtractionRules="@xml/data_extraction_rules" | ||
| android:fullBackupContent="@xml/backup_rules" | ||
| android:icon="@mipmap/ic_launcher" | ||
| android:label="@string/app_name" | ||
| android:roundIcon="@mipmap/ic_launcher_round" | ||
| android:supportsRtl="true" | ||
| android:theme="@style/Theme.MASTestApp" | ||
| tools:targetApi="31"> | ||
| <!-- Vulnerable deep link Activity: exported with no validation --> | ||
| <activity | ||
| android:name=".DeepLinkActivity" | ||
| android:exported="true" | ||
| android:theme="@style/Theme.MASTestApp"> | ||
| <intent-filter> | ||
| <action android:name="android.intent.action.VIEW" /> | ||
| <category android:name="android.intent.category.DEFAULT" /> | ||
| <category android:name="android.intent.category.BROWSABLE" /> | ||
| <data android:scheme="mastestapp" android:host="toggle" /> | ||
| </intent-filter> | ||
| </activity> | ||
| <activity | ||
| android:name=".MainActivity" | ||
| android:exported="true" | ||
| android:windowSoftInputMode="adjustResize" | ||
| android:theme="@style/Theme.MASTestApp"> | ||
| <intent-filter> | ||
| <action android:name="android.intent.action.MAIN" /> | ||
|
|
||
| <category android:name="android.intent.category.LAUNCHER" /> | ||
| </intent-filter> | ||
| </activity> | ||
| </application> | ||
|
|
||
| </manifest> |
Uh oh!
There was an error while loading. Please reload this page.