-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Port MASTG-TEST-0034: Testing Object Persistence (android) (by @appknox) #3259
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
Closed
Closed
Changes from 13 commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
0a6c4e4
Demo and rules added
sk3l10x1ng fc857d5
Added deprecated notes
sk3l10x1ng c14509a
added MASTG-TEST
sk3l10x1ng fa979e7
updated DEMO and test-beta
sk3l10x1ng 6390f7e
fix
sk3l10x1ng c2e932e
fix spell
sk3l10x1ng 11da687
fix
sk3l10x1ng 122545f
fix spelling
sk3l10x1ng 1b332c2
fix id
sk3l10x1ng 57d0920
updated
sk3l10x1ng 4ae5d61
updated
sk3l10x1ng 507fde9
fix
sk3l10x1ng ee83512
fix rule
sk3l10x1ng 3624500
updated DEMO-0041
sk3l10x1ng af3e0d8
updated MastgTest_reversed.java
sk3l10x1ng 75c8938
updated MastgTest.kt
sk3l10x1ng 1ee74f7
updated rules & output
sk3l10x1ng 29665f9
parcelable demo
sk3l10x1ng 01ccacd
updated test-beta
sk3l10x1ng e629569
fix space
sk3l10x1ng fdc2c9e
fix
sk3l10x1ng e3369a3
updated filenames
sk3l10x1ng 48d6e16
updated demos
sk3l10x1ng 70e4cd4
Merge branch 'master' into port-MASTG-TEST-0034
cpholguera f361ca4
updated Object Deserialization Serializable demo
sk3l10x1ng b1e4be2
update filename,rule & deprecation note
sk3l10x1ng 58f9d90
updated Object Deserialisation Serialisable demo
sk3l10x1ng 9046e49
fix files
sk3l10x1ng 61b0f63
fix MastgTest.kt
sk3l10x1ng File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
34 changes: 34 additions & 0 deletions
34
demos/android/MASVS-CODE/MASTG-DEMO-0041/MASTG-DEMO-0041.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| --- | ||
| platform: android | ||
| title: Uses of Object Persistence with semgrep | ||
| id: MASTG-DEMO-0041 | ||
| code: [kotlin] | ||
| test: MASTG-TEST-0266 | ||
| --- | ||
|
|
||
| ### Sample | ||
|
|
||
| The code snippet shows that object persistence being used for storing sensitive information on the device using `org.json.JSONObject` and `org.json.JSONArray`. | ||
|
|
||
| {{ MastgTest.kt # MastgTest_reversed.java }} | ||
|
|
||
| ### Steps | ||
|
|
||
| Let's run @MASTG-TOOL-0110 rules against the sample code. | ||
|
|
||
| {{ ../../../../rules/mastg-android-object-persistence.yml }} | ||
|
|
||
| {{ run.sh }} | ||
|
|
||
| ### Observation | ||
|
|
||
| The output file shows usages of the object persistence in the code. | ||
|
|
||
| {{ output.txt }} | ||
|
|
||
| ### Evaluation | ||
|
|
||
| The test fails because `org.json.JSONObject` and `org.json.JSONArray` were found in the code. | ||
|
|
||
| - Line 15 contains the import of `JSONArray`. | ||
| - Line 16 contains the import of `JSONObject`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| package org.owasp.mastestapp | ||
|
|
||
| import android.content.Context | ||
| import android.util.Log | ||
| import org.json.JSONObject | ||
| import org.json.JSONArray | ||
| import java.io.File | ||
|
|
||
| class MastgTest(private val context: Context) { | ||
|
|
||
| private val sensitiveData = mapOf( | ||
| "username" to "admin", | ||
| "password" to "SuperSecret123!", | ||
| "api_key" to "AKIAIOSFODNN7EXAMPLE", | ||
| "auth_token" to "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." | ||
| ) | ||
|
|
||
| // Vulnerable function: stores sensitive data in an insecure JSON file | ||
| fun storeSensitiveDataInsecurely(): Boolean { | ||
| try { | ||
| val jsonData = JSONObject(sensitiveData as Map<*, *>) | ||
| val file = File(context.filesDir, "config.json") | ||
| file.writeText(jsonData.toString()) | ||
| Log.d("MASTG-TEST", "Sensitive data stored insecurely at: ${file.absolutePath}") | ||
| return true | ||
| } catch (e: Exception) { | ||
| Log.e("MASTG-TEST", "Error storing data", e) | ||
| return false | ||
| } | ||
| } | ||
|
|
||
| // Vulnerable function: loads sensitive data without proper validation | ||
| fun loadSensitiveDataInsecurely(): Map<String, String> { | ||
| try { | ||
| val file = File(context.filesDir, "config.json") | ||
| val jsonString = file.readText() | ||
| val jsonData = JSONObject(jsonString) | ||
|
|
||
| val result = mutableMapOf<String, String>() | ||
| val keys = jsonData.keys() | ||
| while (keys.hasNext()) { | ||
| val key = keys.next() | ||
| result[key] = jsonData.getString(key) | ||
| } | ||
|
|
||
| Log.d("MASTG-TEST", "Loaded sensitive data: $result") | ||
| return result | ||
| } catch (e: Exception) { | ||
| Log.e("MASTG-TEST", "Error loading data", e) | ||
| return emptyMap() | ||
| } | ||
| } | ||
|
|
||
| // Another vulnerable example: storing serialized JSON array with sensitive data | ||
| fun storeSensitiveArrayInsecurely(): Boolean { | ||
| try { | ||
| val jsonArray = JSONArray() | ||
| jsonArray.put(JSONObject(mapOf("credit_card" to "4111111111111111", "cvv" to "123"))) | ||
| jsonArray.put(JSONObject(mapOf("credit_card" to "5555555555554444", "cvv" to "456"))) | ||
|
|
||
| val file = File(context.filesDir, "transactions.json") | ||
| file.writeText(jsonArray.toString()) | ||
| Log.d("MASTG-TEST", "Sensitive array stored insecurely at: ${file.absolutePath}") | ||
| return true | ||
| } catch (e: Exception) { | ||
| Log.e("MASTG-TEST", "Error storing array", e) | ||
| return false | ||
| } | ||
| } | ||
|
|
||
| fun mastgTest(): String { | ||
| // Store sensitive data | ||
| storeSensitiveDataInsecurely() | ||
|
|
||
| // Load sensitive data | ||
| val loadedData = loadSensitiveDataInsecurely() | ||
|
|
||
| // Store sensitive array | ||
| storeSensitiveArrayInsecurely() | ||
|
|
||
| return "MASTG Test completed successfully. Check logs for details." | ||
| } | ||
| } |
91 changes: 91 additions & 0 deletions
91
demos/android/MASVS-CODE/MASTG-DEMO-0041/MastgTest_reversed.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| package org.owasp.mastestapp; | ||
|
|
||
| import android.content.Context; | ||
| import android.util.Log; | ||
| import androidx.autofill.HintConstants; | ||
| import java.io.File; | ||
| import java.util.Iterator; | ||
| import java.util.LinkedHashMap; | ||
| import java.util.Map; | ||
| import kotlin.Metadata; | ||
| import kotlin.TuplesKt; | ||
| import kotlin.collections.MapsKt; | ||
| import kotlin.io.FilesKt; | ||
| import kotlin.jvm.internal.Intrinsics; | ||
| import org.json.JSONArray; | ||
| import org.json.JSONObject; | ||
|
|
||
| /* 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$\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\u000b\n\u0002\b\u0004\b\u0007\u0018\u00002\u00020\u0001B\u000f\u0012\u0006\u0010\u0002\u001a\u00020\u0003¢\u0006\u0004\b\u0004\u0010\u0005J\u0006\u0010\t\u001a\u00020\nJ\u0012\u0010\u000b\u001a\u000e\u0012\u0004\u0012\u00020\b\u0012\u0004\u0012\u00020\b0\u0007J\u0006\u0010\f\u001a\u00020\nJ\u0006\u0010\r\u001a\u00020\bR\u000e\u0010\u0002\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000R\u001a\u0010\u0006\u001a\u000e\u0012\u0004\u0012\u00020\b\u0012\u0004\u0012\u00020\b0\u0007X\u0082\u0004¢\u0006\u0002\n\u0000¨\u0006\u000e"}, d2 = {"Lorg/owasp/mastestapp/MastgTest;", "", "context", "Landroid/content/Context;", "<init>", "(Landroid/content/Context;)V", "sensitiveData", "", "", "storeSensitiveDataInsecurely", "", "loadSensitiveDataInsecurely", "storeSensitiveArrayInsecurely", "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; | ||
| private final Map<String, String> sensitiveData; | ||
|
|
||
| public MastgTest(Context context) { | ||
| Intrinsics.checkNotNullParameter(context, "context"); | ||
| this.context = context; | ||
| this.sensitiveData = MapsKt.mapOf(TuplesKt.to(HintConstants.AUTOFILL_HINT_USERNAME, "admin"), TuplesKt.to(HintConstants.AUTOFILL_HINT_PASSWORD, "SuperSecret123!"), TuplesKt.to("api_key", "AKIAIOSFODNN7EXAMPLE"), TuplesKt.to("auth_token", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...")); | ||
| } | ||
|
|
||
| public final boolean storeSensitiveDataInsecurely() { | ||
| try { | ||
| Map<String, String> map = this.sensitiveData; | ||
| Intrinsics.checkNotNull(map, "null cannot be cast to non-null type kotlin.collections.Map<*, *>"); | ||
| JSONObject jsonData = new JSONObject(map); | ||
| File file = new File(this.context.getFilesDir(), "config.json"); | ||
| String jSONObject = jsonData.toString(); | ||
| Intrinsics.checkNotNullExpressionValue(jSONObject, "toString(...)"); | ||
| FilesKt.writeText$default(file, jSONObject, null, 2, null); | ||
| Log.d("MASTG-TEST", "Sensitive data stored insecurely at: " + file.getAbsolutePath()); | ||
| return true; | ||
| } catch (Exception e) { | ||
| Log.e("MASTG-TEST", "Error storing data", e); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| public final Map<String, String> loadSensitiveDataInsecurely() { | ||
| try { | ||
| File file = new File(this.context.getFilesDir(), "config.json"); | ||
| String jsonString = FilesKt.readText$default(file, null, 1, null); | ||
| JSONObject jsonData = new JSONObject(jsonString); | ||
| Map result = new LinkedHashMap(); | ||
| Iterator keys = jsonData.keys(); | ||
| while (keys.hasNext()) { | ||
| String key = keys.next(); | ||
| result.put(key, jsonData.getString(key)); | ||
| } | ||
| Log.d("MASTG-TEST", "Loaded sensitive data: " + result); | ||
| return result; | ||
| } catch (Exception e) { | ||
| Log.e("MASTG-TEST", "Error loading data", e); | ||
| return MapsKt.emptyMap(); | ||
| } | ||
| } | ||
|
|
||
| public final boolean storeSensitiveArrayInsecurely() { | ||
| try { | ||
| JSONArray jsonArray = new JSONArray(); | ||
| jsonArray.put(new JSONObject(MapsKt.mapOf(TuplesKt.to("credit_card", "4111111111111111"), TuplesKt.to("cvv", "123")))); | ||
| jsonArray.put(new JSONObject(MapsKt.mapOf(TuplesKt.to("credit_card", "5555555555554444"), TuplesKt.to("cvv", "456")))); | ||
| File file = new File(this.context.getFilesDir(), "transactions.json"); | ||
| String jSONArray = jsonArray.toString(); | ||
| Intrinsics.checkNotNullExpressionValue(jSONArray, "toString(...)"); | ||
| FilesKt.writeText$default(file, jSONArray, null, 2, null); | ||
| Log.d("MASTG-TEST", "Sensitive array stored insecurely at: " + file.getAbsolutePath()); | ||
| return true; | ||
| } catch (Exception e) { | ||
| Log.e("MASTG-TEST", "Error storing array", e); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| public final String mastgTest() { | ||
| storeSensitiveDataInsecurely(); | ||
| loadSensitiveDataInsecurely(); | ||
| storeSensitiveArrayInsecurely(); | ||
| return "MASTG Test completed successfully. Check logs for details."; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
|
|
||
|
|
||
| ┌─────────────────┐ | ||
| │ 2 Code Findings │ | ||
| └─────────────────┘ | ||
|
|
||
| MastgTest_reversed.java | ||
| ❯❱ rules.mastg-android-object-persistence | ||
| [MASVS-CODE-4] The application make use of Object Serialization in the code. | ||
|
|
||
| 15┆ import org.json.JSONArray; | ||
| ⋮┆---------------------------------------- | ||
| 16┆ import org.json.JSONObject; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| NO_COLOR=true semgrep -c ../../../../rules/mastg-android-object-persistence.yml ./MastgTest_reversed.java --text -o output.txt |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| rules: | ||
| - id: mastg-android-object-persistence | ||
| severity: WARNING | ||
| languages: | ||
| - java | ||
| metadata: | ||
| summary: This rule looks for use of Object Serialization. | ||
| message: "[MASVS-CODE-4] The application make use of Object Serialization in the code." | ||
| patterns: | ||
| - pattern-either: | ||
| - pattern: import org.json.JSONArray; | ||
| - pattern: import org.json.JSONObject; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| --- | ||
| title: Use of Object Persistence using JSON | ||
| platform: android | ||
| id: MASTG-TEST-0266 | ||
| type: [static] | ||
| weakness: MASWE-0088 | ||
| --- | ||
|
|
||
| ## Overview | ||
|
|
||
| Android provides the JSONObject and JSONArray classes, which are essential for handling JSON data. JSON or JavaScript Object Notation, is a lightweight format for data interchange that is both human-readable and easy for machines to parse and generate. One important characteristic of JSON representations is that they are based on strings, making them immutable. This immutability means that once a JSON object is created, it cannot be changed. As a result, if sensitive information is stored within a JSON structure, it becomes more difficult to completely remove that information from memory, posing potential security risks. Additionally, JSON data can be stored in various locations, including NoSQL databases, which are designed to handle unstructured data, or in files on a local or remote file system, providing flexibility in how data is managed and accessed. | ||
|
|
||
| ## Steps | ||
|
|
||
| 1. Run a static analysis tool such as @MASTG-TOOL-0110 on the code and look for uses of the `JSONObject` and `JSONArray`. | ||
|
|
||
| ## Observation | ||
|
|
||
| The output file shows usages of the object Persistence using `JSONObject` and `JSONArray` in the code. | ||
|
|
||
| ## Evaluation | ||
|
|
||
| The test fails if the `org.json.JSONObject` and `org.json.JSONArray` was found in the code. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
We shouldn't simply test for the use of
JSONObjectandJSONArrayas this isn't a vulnerability in itself. While it's true that JSON may hold sensitive data, its presence alone is not indicative of a security issue. What actually matters in the context of this MASVS-CODE control is the risk of unsafe deserialization, where attacker-controlled input is used to reconstruct objects, potentially triggering unintended behavior or code execution.This is a common and well-documented threat in Android, especially when using custom classes with
Serializable,Parcelable, or reflection-based deserialization.The current proposal in this PR seems to deal with other concerns like data persistence or secure storage. These concerns are already covered under MASVS-STORAGE and MASVS-CRYPTO, so this test should focus specifically on identifying and mitigating deserialization risks in the app logic.
I totally see how this can be confusing when reading the original test (MASTG-TEST-0034). I went thought it now, took the relevant excerpts for you and explained why it relates to unsafe deserialization:
This relates to unsafe deserialization because serialized data can be intercepted or modified by attackers, leading to code execution or data tampering if deserialized without validation.
This is relevant because lack of validation of deserialized data may trigger unintended behavior or exploit logic flaws in the application.
Reflection-based deserialization combined with
Serializablecan allow attackers to manipulate inputs and trigger arbitrary code paths, a classic unsafe deserialization vector.This indicates that the app is using Java serialization, which could enable a deserialization attack when handling untrusted data.
See "Risk: Unwanted Object Deserialization".
Monitoring how objects are deserialized may help detecting unsafe behavior, such as the use of untrusted input to reconstruct objects. While relevant, I think you can skip this part of the test (the dynamic testing) for now and focus on static.
Parcelables are Android’s custom serialization mechanism. If used without proper access control, attackers can inject malicious objects through IPC and exploit unsafe deserialization.
See Risk: Deserialization of untrusted input
Suggestion