Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions demos/android/MASVS-RESILIENCE/MASTG-DEMO-0x40/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?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">
<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>
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?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: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="androidx.compose.ui.tooling.PreviewActivity"
android:exported="true"/>
<activity
android:theme="@android:style/Theme.Material.Light.NoActionBar"
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>
36 changes: 36 additions & 0 deletions demos/android/MASVS-RESILIENCE/MASTG-DEMO-0x40/MASTG-DEMO-0x40.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
platform: android
title: JDWP Debugging Checks in App Code
id: MASTG-DEMO-0x40
code: [kotlin]
test: MASTG-TEST-0046-1
tools: [MASTG-TOOL-0110]
---

## Sample

This sample checks whether the app is debuggable via `ApplicationInfo.FLAG_DEBUGGABLE` and whether a debugger is attached via `Debug.isDebuggerConnected()`.

{{ MastgTest.kt # MastgTest_reversed.java }}

## Steps

Run the static analysis rule and manifest check against the decompiled code and manifest.

{{ ../../../../rules/mastg-android-debugger-checks.yml }}
{{ ../../../../rules/mastg-android-debuggable-flag.yml }}

{{ run.sh }}

## Observation

The output lists code locations where debugger checks are performed and whether the manifest is marked as debuggable.

{{ output.txt }}

## Evaluation

Review each reported location:

- `Debug.isDebuggerConnected()` is reported in `MastgTest_reversed.java` (line 25), which indicates a runtime debugger check.
- The `android:debuggable="true"` manifest flag is reported in `AndroidManifest_reversed.xml` (line 22), which indicates the app build allows debugging.
46 changes: 46 additions & 0 deletions demos/android/MASVS-RESILIENCE/MASTG-DEMO-0x40/MastgTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.owasp.mastestapp

// SUMMARY: This demo checks debuggable status and active JDWP debugging signals.

import android.content.Context
import android.content.pm.ApplicationInfo
import android.os.Debug

class MastgTest (private val context: Context){

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

try {
val debuggable = isDebuggable(context)
val debuggerConnected = Debug.isDebuggerConnected()

// FAIL: [MASTG-TEST-0046-1] android:debuggable enabled in the app flags.
// PASS: [MASTG-TEST-0046-1] android:debuggable disabled in the app flags.
if (debuggable) {
r.add(Status.FAIL, "android:debuggable appears enabled via ApplicationInfo.FLAG_DEBUGGABLE.")
} else {
r.add(Status.PASS, "android:debuggable appears disabled (FLAG_DEBUGGABLE not set).")
}

// FAIL: [MASTG-TEST-0046-1] Debug.isDebuggerConnected reports a debugger.
// PASS: [MASTG-TEST-0046-1] Debug.isDebuggerConnected reports no debugger.
if (debuggerConnected) {
r.add(Status.FAIL, "Debug.isDebuggerConnected reports an attached debugger.")
} else {
r.add(Status.PASS, "Debug.isDebuggerConnected reports no debugger attached.")
}

} catch (e: Exception) {
r.add(Status.ERROR, "Unexpected error while running anti-debugging checks: ${e.javaClass.simpleName}: ${e.message}")
}

return r.toJson()
}

private fun isDebuggable(context: Context): Boolean {
val appInfo = context.applicationContext.applicationInfo
return (appInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.owasp.mastestapp;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Debug;
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;

/* compiled from: MastgTest.kt */
@Metadata(d1 = {"\u0000\u001e\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\u000b\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\u0010\u0010\b\u001a\u00020\t2\u0006\u0010\u0002\u001a\u00020\u0003H\u0002R\u000e\u0010\u0002\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000¨\u0006\n"}, d2 = {"Lorg/owasp/mastestapp/MastgTest;", "", "context", "Landroid/content/Context;", "<init>", "(Landroid/content/Context;)V", "mastgTest", "", "isDebuggable", "", "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() {
DemoResults r = new DemoResults("0028");
try {
boolean debuggable = isDebuggable(this.context);
boolean debuggerConnected = Debug.isDebuggerConnected();
if (debuggable) {
r.add(Status.FAIL, "android:debuggable appears enabled via ApplicationInfo.FLAG_DEBUGGABLE.");
} else {
r.add(Status.PASS, "android:debuggable appears disabled (FLAG_DEBUGGABLE not set).");
}
if (debuggerConnected) {
r.add(Status.FAIL, "Debug.isDebuggerConnected reports an attached debugger.");
} else {
r.add(Status.PASS, "Debug.isDebuggerConnected reports no debugger attached.");
}
} catch (Exception e) {
r.add(Status.ERROR, "Unexpected error while running anti-debugging checks: " + e.getClass().getSimpleName() + ": " + e.getMessage());
}
return r.toJson();
}

private final boolean isDebuggable(Context context) {
ApplicationInfo appInfo = context.getApplicationContext().getApplicationInfo();
return (appInfo.flags & 2) != 0;
}
}
24 changes: 24 additions & 0 deletions demos/android/MASVS-RESILIENCE/MASTG-DEMO-0x40/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@


┌────────────────┐
│ 1 Code Finding │
└────────────────┘

mastg/demos/android/MASVS-RESILIENCE/MASTG-DEMO-0x40/MastgTest_reversed.java
❯❱ mastg.rules.mastg-android-debugger-checks
[MASVS-RESILIENCE-4] debugger checks detected.

25┆ boolean debuggerConnected = Debug.isDebuggerConnected();



┌────────────────┐
│ 1 Code Finding │
└────────────────┘

mastg/demos/android/MASVS-RESILIENCE/MASTG-DEMO-0x40/AndroidManifest_reversed.xml
❯❱ mastg.rules.mastg-android-debuggable-flag
[MASVS-RESILIENCE-4] debuggable detected as true.

22┆ android:debuggable="true"

5 changes: 5 additions & 0 deletions demos/android/MASVS-RESILIENCE/MASTG-DEMO-0x40/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail

NO_COLOR=true semgrep -c ../../../../rules/mastg-android-debugger-checks.yml ./MastgTest_reversed.java --text > output.txt
NO_COLOR=true semgrep -c ../../../../rules/mastg-android-debuggable-flag.yml ./AndroidManifest_reversed.xml --text >> output.txt
2 changes: 1 addition & 1 deletion knowledge/android/MASVS-RESILIENCE/MASTG-KNOW-0028.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ u0_a271 11657 573 4302108 50600 ptrace_stop 0 t com.example.hell
$ adb shell cat /proc/11657/status | grep -e "^TracerPid:" | sed "s/^TracerPid:\t//"
TracerPid: 11839
$ adb shell ps -A | grep 11839
u0_a271 11839 11837 14024 4548 poll_schedule_timeout 0 S lldb-server
root 11839 11837 14024 4548 poll_schedule_timeout 0 S lldb-server
```

You can see how the status file of com.example.hellojni (PID=11657) contains a TracerPID of 11839, which we can identify as the lldb-server process.
Expand Down
2 changes: 1 addition & 1 deletion rules/mastg-android-debuggable-flag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ rules:
summary: This rule inspects the AndroidManifest.xml for the debuggable flag.
message: "[MASVS-RESILIENCE-4] debuggable detected as $ARG."
patterns:
- pattern: 'android:debuggable="$ARG"
- pattern: 'android:debuggable="$ARG"'
12 changes: 12 additions & 0 deletions rules/mastg-android-debugger-checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
rules:
- id: mastg-android-debugger-checks
severity: WARNING
languages: [java]
metadata:
summary: Detects Android debugger checks in code.
message: "[MASVS-RESILIENCE-4] debugger checks detected."
patterns:
- pattern-either:
- pattern: Debug.isDebuggerConnected(...)
- pattern: Debug.isDebuggerConnected()
- pattern: ApplicationInfo.FLAG_DEBUGGABLE
Loading