-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
MASTG v1->v2 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
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-0062 | ||
code: [kotlin] | ||
test: MASTG-TEST-0288 | ||
status: new | ||
--- | ||
|
||
### 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) | ||
} | ||
} | ||
} |
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 > output.txt |
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-0063 | ||
code: [kotlin] | ||
test: MASTG-TEST-0289 | ||
status: new | ||
--- | ||
|
||
### Sample | ||
|
||
The following is a sample `AndroidManifest.xml` snippet that defines a deep link intent filter without the `android:autoVerify="true"` attribute. | ||
|
||
{{ ../MASTG-DEMO-0062/AndroidManifest_reversed.xml }} | ||
|
||
### Steps | ||
|
||
Let's run @MASTG-TOOL-0110 rules against the sample manifest. | ||
|
||
{{ ../../../../rules/mastg-android-autoverify-missing.yml }} | ||
|
||
{{ run.sh }} | ||
|
||
### Observation | ||
|
||
The rule has identified that the deep link intent filter is missing the `android:autoVerify="true"` attribute. | ||
|
||
{{ output.txt }} | ||
|
||
### Evaluation | ||
|
||
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
rules: | ||
- id: android-autoverify-missing | ||
severity: WARNING | ||
languages: | ||
- xml | ||
metadata: | ||
summary: This rule looks for insecure deep link configurations. | ||
message: '[MASVS-PLATFORM] Deep link intent filter missing android:autoVerify="true",enabling malicious apps to hijack links.' | ||
patterns: | ||
- pattern-inside: | | ||
<activity ...> | ||
... | ||
</activity> | ||
- pattern: | | ||
<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="..." /> | ||
</intent-filter> | ||
- pattern-not: | | ||
<intent-filter android:autoVerify="true"> | ||
... | ||
</intent-filter> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
rules: | ||
- id: android-unvalidated-deeplink-data | ||
severity: WARNING | ||
languages: | ||
- java | ||
metadata: | ||
summary: This rule looks for insecure deep link configurations. | ||
message: "[MASVS-PLATFORM] Unvalidated deep link query parameters are directly loaded into a WebView." | ||
mode: taint | ||
pattern-sources: | ||
# Source: Data originating from the intent's URI parameter. | ||
- pattern: $URI.getQueryParameter(...) | ||
pattern-sinks: | ||
# Sink: The unvalidated data is loaded into a WebView. | ||
- pattern: $WEBVIEW.loadUrl(...) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
--- | ||
title: Unvalidated URL from Deep Link Loaded in WebView | ||
platform: android | ||
id: MASTG-TEST-0292 | ||
type: [static] | ||
weakness: MASWE-0088 | ||
profiles: [L1, L2] | ||
--- | ||
|
||
### Overview | ||
|
||
This vulnerability arises when an app accepts a URL from an external source such as a deep link query parameter and loads it into a WebView without validation. An attacker can craft a malicious Intent containing a deep link with a harmful URL. When loaded, the WebView executes the embedded script in the app's context, resulting in a Cross-Site Scripting (XSS) vulnerability. This could allow theft of session cookies, injection of fake content, or unauthorized actions on behalf of the user. | ||
|
||
### Steps | ||
|
||
Run a static ancalysis tool such as @MASTG-TOOL-0110 on the codebase to detect data flows from deep link parameters (e.g., `getQueryParameter()`) to dangerous sinks (e.g., `WebView.loadUrl()`). | ||
|
||
### Observation | ||
|
||
The output file shows a data flow where data from an Intent is used in `WebView.loadUrl()` without prior sanitization or validation. | ||
|
||
### Evaluation | ||
|
||
The test fails due to the application loading an unvalidated URL from an untrusted Intent extra into a WebView. A malicious application can create an Intent with a deep link containing a URL pointing to a malicious website. When this URL is loaded by the vulnerable WebView, the user is redirected to the attacker's site. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
--- | ||
title: Deep Link Intent Filter Missing android:autoVerify | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be "Use of Unverified App Links" plus an additional "Use of Unverified Custom URL Schemes" which is now missing. |
||
platform: android | ||
id: MASTG-TEST-0293 | ||
type: [static] | ||
weakness: MASWE-0058 | ||
profiles: [L1, L2] | ||
--- | ||
|
||
### Overview | ||
|
||
This vulnerability occurs when a deep link intent filter in `AndroidManifest.xml` lacks the `android:autoVerify="true"` attribute. Without verification, Android cannot confirm the app's ownership of the declared domain. A malicious app could register the same intent filter and intercept deep links, enabling phishing, credential theft, or hijacking of user actions. | ||
|
||
### Steps | ||
|
||
Run a static analysis tool such as @MASTG-TOOL-0110 on the `AndroidManifest.xml` to detect deep link intent filters that are missing the `android:autoVerify="true"` attribute. | ||
|
||
### Observation | ||
|
||
The output shows a `<intent-filter>` that define deep links but do not include the `android:autoVerify="true"` attribute. | ||
|
||
### Evaluation | ||
|
||
The test fails as App Links verification is not enforced. Without `android:autoVerify="true"`, malicious apps can hijack deep links and redirect users to attacker-controlled content. |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ScreaMy7 I see that a lot of the information contained in this test is being lost right now. Please consider porting it all:
|
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.
the right MASWE should be MASWE-0071: WebViews Loading Content from Untrusted Sources