-
-
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 all 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,36 @@ | ||
| <?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:icon="@mipmap/ic_launcher" | ||
| android:label="@string/app_name" | ||
| android:roundIcon="@mipmap/ic_launcher_round" | ||
| android:supportsRtl="true" | ||
| android:theme="@style/Theme.MASTestApp" | ||
| android:usesCleartextTraffic="true" | ||
| tools:targetApi="31"> | ||
|
|
||
| <activity | ||
| android:name=".MainActivity" | ||
| 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:host="deeplink" | ||
| android:scheme="vulnerable-app" /> | ||
| </intent-filter> | ||
| <intent-filter> | ||
| <action android:name="android.intent.action.MAIN" /> | ||
| <category android:name="android.intent.category.LAUNCHER" /> | ||
| </intent-filter> | ||
| </activity> | ||
|
|
||
| </application> | ||
| </manifest> |
| 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 | ||
|
|
||
| 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 | ||
|
|
||
| Let's run @MASTG-TOOL-0110 rules against the sample code. | ||
|
|
||
| {{ ../../../../rules/mastg-android-unvalidated-deeplink-data.yml }} | ||
|
|
||
| {{ run.sh }} | ||
|
|
||
| ## Observation | ||
|
|
||
| The output file shows usage of dangerous data flow from a source `getQueryParameter` to a sink `loadUrl`. | ||
|
|
||
| {{ output.txt }} | ||
|
|
||
| ## Evaluation | ||
|
|
||
| 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 |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,36 @@ | ||||||||||
| <?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:icon="@mipmap/ic_launcher" | ||||||||||
| android:label="@string/app_name" | ||||||||||
| android:roundIcon="@mipmap/ic_launcher_round" | ||||||||||
| android:supportsRtl="true" | ||||||||||
| android:theme="@style/Theme.MASTestApp" | ||||||||||
| android:usesCleartextTraffic="true" | ||||||||||
| tools:targetApi="31"> | ||||||||||
|
|
||||||||||
| <activity | ||||||||||
| android:name=".MainActivity" | ||||||||||
| 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:host="deeplink" | ||||||||||
| android:scheme="vulnerable-app" /> | ||||||||||
|
Comment on lines
+26
to
+27
|
||||||||||
| android:host="deeplink" | |
| android:scheme="vulnerable-app" /> | |
| android:host="deeplink.example.com" | |
| android:scheme="https" /> |
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"/> |
Uh oh!
There was an error while loading. Please reload this page.