diff --git a/.github/workflows/build-android-demos.yml b/.github/workflows/build-android-demos.yml
index 31ca22599bb..10ef94d4a40 100644
--- a/.github/workflows/build-android-demos.yml
+++ b/.github/workflows/build-android-demos.yml
@@ -173,6 +173,14 @@ jobs:
cp -f "$demo/data_extraction_rules.xml" MASTestApp-Android/app/src/main/res/xml/data_extraction_rules.xml 2>/dev/null \
&& echo "Copied data_extraction_rules.xml for $demo" \
|| echo "No data_extraction_rules.xml found for $demo"
+
+ if [ -f "$demo/build.gradle.kts.libs" ]; then
+ libs_content=$(cat "$demo/build.gradle.kts.libs")
+ sed -i "s|// ADD_LIBS_HERE|$libs_content|g" MASTestApp-Android/app/build.gradle.kts
+ echo "Replaced dependencies in build.gradle.kts for $demo"
+ else
+ echo "No build.gradle.kts.libs found for $demo, skipping dependencies replacement"
+ fi
echo "Building APK for $demo"
cd MASTestApp-Android
diff --git a/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/MASTG-DEMO-0060.md b/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/MASTG-DEMO-0060.md
index 466e16a7bda..9b57dee3edb 100644
--- a/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/MASTG-DEMO-0060.md
+++ b/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/MASTG-DEMO-0060.md
@@ -5,7 +5,46 @@ id: MASTG-DEMO-0060
code: [kotlin]
test: MASTG-TEST-0287
kind: pass
-status: placeholder
-note: This demo shows how to store sensitive data securely in the app sandbox using the EncryptedSharedPreferences class.
---
+### Sample
+
+The code snippet below shows sample code which stores sensitive data using `String` and `StringSet` from `EncryptedSharedPreferences`.
+
+!!! Warning
+
+ The **Jetpack security crypto library**, including the `EncryptedFile` and `EncryptedSharedPreferences` classes, has been [deprecated](https://developer.android.com/privacy-and-security/cryptography#jetpack_security_crypto_library). However, since an official replacement has not yet been released, we recommend using these classes until one is available.
+
+{{ MastgTest.kt # build.gradle.kts.libs }}
+
+### Steps
+
+1. Install the app on a device (@MASTG-TECH-0005)
+2. Make sure you have @MASTG-TOOL-0001 installed on your machine and the frida-server running on the device
+3. Run `run.sh` to spawn the app with Frida
+4. Click the **Start** button
+5. Stop the script by pressing `Ctrl+C` and/or `q` to quit the Frida CLI
+
+{{ hooks.js # run.sh }}
+
+### Observation
+
+The output shows all instances of strings written using `EncryptedSharedPreferences` via `SharedPreferences` that were found at runtime. A backtrace is also provided to help identify the location in the code.
+
+{{ output.json }}
+
+### Evaluation
+
+This test **passes** because sensitive data is stored using `EncryptedSharedPreferences`, which encrypts both keys and values at rest. Even if an attacker gains access to the app's sandbox, the data remains encrypted.
+
+For example, to confirm this, run the following command:
+
+```sh
+adb shell cat /data/data/org.owasp.mastestapp/shared_prefs/MasSharedPref_Sensitive_Data.xml
+```
+
+Which returns:
+
+{{ MasSharedPref_Sensitive_Data.xml }}
+
+The actual values are not visible in plain text, confirming that encryption is applied.
diff --git a/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/MasSharedPref_Sensitive_Data.xml b/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/MasSharedPref_Sensitive_Data.xml
new file mode 100644
index 00000000000..45f868313b9
--- /dev/null
+++ b/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/MasSharedPref_Sensitive_Data.xml
@@ -0,0 +1,8 @@
+
+
diff --git a/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/MastgTest.kt b/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/MastgTest.kt
new file mode 100644
index 00000000000..0738d2941cd
--- /dev/null
+++ b/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/MastgTest.kt
@@ -0,0 +1,43 @@
+package org.owasp.mastestapp
+
+import android.content.Context
+import androidx.core.content.edit
+import androidx.security.crypto.EncryptedSharedPreferences
+import androidx.security.crypto.MasterKey
+
+class MastgTest(private val context: Context) {
+ // WARNING: In a real application, these keys should NOT be hardcoded. They should be stored securely, for instance, in the Android Keystore.
+ private val awsKey = "AKIAABCDEFGHIJKLMNOP"
+ private val githubToken = "ghp_1234567890abcdefghijklmnopqrstuvABCD"
+ private val preSharedKeys = hashSetOf(
+ "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALfX7kbfFv3pc3JjOHQ=\n-----END PRIVATE KEY-----",
+ "-----BEGIN PRIVATE KEY-----\ngJXS9EwpuzK8U1TOgfplwfKEVngCE2D5FNBQWvNmuHHbigmTCabsA=\n-----END PRIVATE KEY-----"
+ )
+ private val sharedPrefsName = "MasSharedPref_Sensitive_Data"
+
+ fun mastgTest(): String {
+ return try {
+ val masterKey = MasterKey.Builder(context)
+ .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
+ .build()
+
+ val encryptedPrefs = EncryptedSharedPreferences.create(
+ context,
+ sharedPrefsName,
+ masterKey,
+ EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
+ EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
+ )
+
+ encryptedPrefs.edit {
+ putString("EncryptedAWSKey", awsKey)
+ putString("GitHubToken", githubToken)
+ putStringSet("preSharedKeys", preSharedKeys)
+ }
+
+ "Sensitive data has been written and deleted in the sandbox."
+ } catch (e: Exception) {
+ "Error during MastgTest: ${e.message ?: "Unknown error"}"
+ }
+ }
+}
diff --git a/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/build.gradle.kts.libs b/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/build.gradle.kts.libs
new file mode 100644
index 00000000000..c2638309b76
--- /dev/null
+++ b/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/build.gradle.kts.libs
@@ -0,0 +1 @@
+implementation("androidx.security:security-crypto:1.1.0")
\ No newline at end of file
diff --git a/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/hooks.js b/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/hooks.js
new file mode 100644
index 00000000000..15dcbeac650
--- /dev/null
+++ b/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/hooks.js
@@ -0,0 +1,20 @@
+var target = {
+ category: "STORAGE",
+ demo: "0060",
+ hooks: [
+ {
+ class: "androidx.security.crypto.EncryptedSharedPreferences$Editor",
+ methods: [
+ "putString",
+ "putStringSet"
+ ]
+ },
+ {
+ class: "android.app.SharedPreferencesImpl$EditorImpl",
+ methods: [
+ "putString",
+ "putStringSet"
+ ]
+ }
+ ]
+}
diff --git a/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/output.json b/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/output.json
new file mode 100644
index 00000000000..748974bfffb
--- /dev/null
+++ b/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/output.json
@@ -0,0 +1,258 @@
+{
+ "id": "219ea51c-506a-45f0-a295-34febdad2ca5",
+ "category": "STORAGE",
+ "time": "2025-09-22T20:53:10.158Z",
+ "class": "android.app.SharedPreferencesImpl$EditorImpl",
+ "method": "putString",
+ "stackTrace": [
+ "android.app.SharedPreferencesImpl$EditorImpl.putString(Native Method)",
+ "com.google.crypto.tink.integration.android.SharedPrefKeysetWriter.write(SharedPrefKeysetWriter.java:70)",
+ "com.google.crypto.tink.KeysetHandle.write(KeysetHandle.java:158)",
+ "com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.readOrGenerateNewKeyset(AndroidKeysetManager.java:299)",
+ "com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.build(AndroidKeysetManager.java:238)",
+ "androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:155)",
+ "androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:120)",
+ "org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:23)"
+ ],
+ "inputParameters": [
+ {
+ "type": "java.lang.String",
+ "value": "__androidx_security_crypto_encrypted_prefs_key_keyset__"
+ },
+ {
+ "type": "java.lang.String",
+ "value": "12a901f5a4c43d49ce7ca6c207fb4d3f7711aba5131cbbd7951bd838c6c154e3528704bf527d554ad3facc098ad4645b1d2ed0a707c1c28f12ebf24e5a9ea08c57088e7c1b43ee84272605fe1ecbf9426509054974640b23e273b73d21a1d48029e4ef3787b6b1fc9c31d590b7e37663ea81e3135b3f0d3eb067148fcbb0e37b7d5909ad7a2cfc55bc11b4f8c00e79cebbf5cbeee2aca68f7558a65b86e888aa343caeea9551d82895a0956c1a4408cdaecffd04123c0a30747970652e676f6f676c65617069732e636f6d2f676f6f676c652e63727970746f2e74696e6b2e4165735369764b6579100118cdaecffd042001"
+ }
+ ],
+ "returnValue": [
+ {
+ "type": "android.content.SharedPreferences$Editor",
+ "value": ""
+ }
+ ]
+}
+{
+ "id": "2726948e-87f4-4088-a4fd-b5a3facfe460",
+ "category": "STORAGE",
+ "time": "2025-09-22T20:53:10.203Z",
+ "class": "android.app.SharedPreferencesImpl$EditorImpl",
+ "method": "putString",
+ "stackTrace": [
+ "android.app.SharedPreferencesImpl$EditorImpl.putString(Native Method)",
+ "com.google.crypto.tink.integration.android.SharedPrefKeysetWriter.write(SharedPrefKeysetWriter.java:70)",
+ "com.google.crypto.tink.KeysetHandle.write(KeysetHandle.java:158)",
+ "com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.readOrGenerateNewKeyset(AndroidKeysetManager.java:299)",
+ "com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.build(AndroidKeysetManager.java:238)",
+ "androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:160)",
+ "androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:120)",
+ "org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:23)"
+ ],
+ "inputParameters": [
+ {
+ "type": "java.lang.String",
+ "value": "__androidx_security_crypto_encrypted_prefs_value_keyset__"
+ },
+ {
+ "type": "java.lang.String",
+ "value": "1286013ebf2688905cdc7062581d934cf22d866c35f5b0c3a4617ef00a4b44c2a33bb9f8c49e2a82535763dadee05ed2587811270339d066bc335db019ab60a68108652cd14a90a77b324a90c5d9ce8fae1a2da0f317b10d71e92c49ae9c0a17ec321e6f3a0c4edcb41580f16e93adfea98518127706dfbee4bef3af57f7419fd7216e89efea8b3f3d1a4208fbc18150123b0a30747970652e676f6f676c65617069732e636f6d2f676f6f676c652e63727970746f2e74696e6b2e41657347636d4b6579100118fbc181502001"
+ }
+ ],
+ "returnValue": [
+ {
+ "type": "android.content.SharedPreferences$Editor",
+ "value": ""
+ }
+ ]
+}
+{
+ "id": "9c495048-f0db-405b-807b-01e0cfbab573",
+ "category": "STORAGE",
+ "time": "2025-09-22T20:53:10.220Z",
+ "class": "android.app.SharedPreferencesImpl$EditorImpl",
+ "method": "putString",
+ "stackTrace": [
+ "android.app.SharedPreferencesImpl$EditorImpl.putString(Native Method)",
+ "androidx.security.crypto.EncryptedSharedPreferences$Editor.putEncryptedObject(EncryptedSharedPreferences.java:378)",
+ "androidx.security.crypto.EncryptedSharedPreferences$Editor.putString(EncryptedSharedPreferences.java:244)",
+ "androidx.security.crypto.EncryptedSharedPreferences$Editor.putString(Native Method)",
+ "org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:32)",
+ "org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$6$lambda$5(MainActivity.kt:55)",
+ "org.owasp.mastestapp.MainActivityKt.$r8$lambda$PtKdgqcXvbS9cMNZVWq3K3GGQKQ(Unknown Source:0)",
+ "org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0)"
+ ],
+ "inputParameters": [
+ {
+ "type": "java.lang.String",
+ "value": "AU+z103BuqUQYnpJY8AyhGP4jbkz01jtCY5EQGG4pVwOPhVv"
+ },
+ {
+ "type": "java.lang.String",
+ "value": "AQoAYPs27SgygktthcLjII2K6yyUMBoY8F59mzWbE1ps2WT5P87ChxB0QF8R4UuBc6WRy+9R0qwpD3WOKw=="
+ }
+ ],
+ "returnValue": [
+ {
+ "type": "android.content.SharedPreferences$Editor",
+ "value": ""
+ }
+ ]
+}
+{
+ "id": "60d32d3c-210a-4a44-926f-874e02ed02b0",
+ "category": "STORAGE",
+ "time": "2025-09-22T20:53:10.214Z",
+ "class": "androidx.security.crypto.EncryptedSharedPreferences$Editor",
+ "method": "putString",
+ "stackTrace": [
+ "androidx.security.crypto.EncryptedSharedPreferences$Editor.putString(Native Method)",
+ "org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:32)",
+ "org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$6$lambda$5(MainActivity.kt:55)",
+ "org.owasp.mastestapp.MainActivityKt.$r8$lambda$PtKdgqcXvbS9cMNZVWq3K3GGQKQ(Unknown Source:0)",
+ "org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0)",
+ "java.lang.Thread.run(Thread.java:1012)"
+ ],
+ "inputParameters": [
+ {
+ "type": "java.lang.String",
+ "value": "EncryptedAWSKey"
+ },
+ {
+ "type": "java.lang.String",
+ "value": "AKIAABCDEFGHIJKLMNOP"
+ }
+ ],
+ "returnValue": [
+ {
+ "type": "android.content.SharedPreferences$Editor",
+ "value": ""
+ }
+ ]
+}
+{
+ "id": "13976db9-4f60-4483-b2e3-46b0399f0222",
+ "category": "STORAGE",
+ "time": "2025-09-22T20:53:10.228Z",
+ "class": "android.app.SharedPreferencesImpl$EditorImpl",
+ "method": "putString",
+ "stackTrace": [
+ "android.app.SharedPreferencesImpl$EditorImpl.putString(Native Method)",
+ "androidx.security.crypto.EncryptedSharedPreferences$Editor.putEncryptedObject(EncryptedSharedPreferences.java:378)",
+ "androidx.security.crypto.EncryptedSharedPreferences$Editor.putString(EncryptedSharedPreferences.java:244)",
+ "androidx.security.crypto.EncryptedSharedPreferences$Editor.putString(Native Method)",
+ "org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:33)",
+ "org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$6$lambda$5(MainActivity.kt:55)",
+ "org.owasp.mastestapp.MainActivityKt.$r8$lambda$PtKdgqcXvbS9cMNZVWq3K3GGQKQ(Unknown Source:0)",
+ "org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0)"
+ ],
+ "inputParameters": [
+ {
+ "type": "java.lang.String",
+ "value": "AU+z101V35Id7aRshk1QOi8IHsdlvcPXjNA885hbFak="
+ },
+ {
+ "type": "java.lang.String",
+ "value": "AQoAYPt6nO+4SoPqUjsZNEYzlPIZn7LtawLzrii1G0XDtQyVJOaSNNwo4OHjYx6CrYcOaSzjr8Fii72fhCSA2KTE5j0BphTnYoc0gz5j3CO/"
+ }
+ ],
+ "returnValue": [
+ {
+ "type": "android.content.SharedPreferences$Editor",
+ "value": ""
+ }
+ ]
+}
+{
+ "id": "29fc3b94-48ce-4d93-86ba-2a16d88ba1c5",
+ "category": "STORAGE",
+ "time": "2025-09-22T20:53:10.222Z",
+ "class": "androidx.security.crypto.EncryptedSharedPreferences$Editor",
+ "method": "putString",
+ "stackTrace": [
+ "androidx.security.crypto.EncryptedSharedPreferences$Editor.putString(Native Method)",
+ "org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:33)",
+ "org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$6$lambda$5(MainActivity.kt:55)",
+ "org.owasp.mastestapp.MainActivityKt.$r8$lambda$PtKdgqcXvbS9cMNZVWq3K3GGQKQ(Unknown Source:0)",
+ "org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0)",
+ "java.lang.Thread.run(Thread.java:1012)"
+ ],
+ "inputParameters": [
+ {
+ "type": "java.lang.String",
+ "value": "GitHubToken"
+ },
+ {
+ "type": "java.lang.String",
+ "value": "ghp_1234567890abcdefghijklmnopqrstuvABCD"
+ }
+ ],
+ "returnValue": [
+ {
+ "type": "android.content.SharedPreferences$Editor",
+ "value": ""
+ }
+ ]
+}
+{
+ "id": "e5aa1f47-435e-458a-93ea-37b8c4d2ef1b",
+ "category": "STORAGE",
+ "time": "2025-09-22T20:53:10.236Z",
+ "class": "android.app.SharedPreferencesImpl$EditorImpl",
+ "method": "putString",
+ "stackTrace": [
+ "android.app.SharedPreferencesImpl$EditorImpl.putString(Native Method)",
+ "androidx.security.crypto.EncryptedSharedPreferences$Editor.putEncryptedObject(EncryptedSharedPreferences.java:378)",
+ "androidx.security.crypto.EncryptedSharedPreferences$Editor.putStringSet(EncryptedSharedPreferences.java:270)",
+ "androidx.security.crypto.EncryptedSharedPreferences$Editor.putStringSet(Native Method)",
+ "org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:34)",
+ "org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$6$lambda$5(MainActivity.kt:55)",
+ "org.owasp.mastestapp.MainActivityKt.$r8$lambda$PtKdgqcXvbS9cMNZVWq3K3GGQKQ(Unknown Source:0)",
+ "org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0)"
+ ],
+ "inputParameters": [
+ {
+ "type": "java.lang.String",
+ "value": "AU+z101iVWAe4pyZC7djOC9ugpEv9bxXAahqi4DNvuxcyA=="
+ },
+ {
+ "type": "java.lang.String",
+ "value": "AQoAYPv4h47i7VnSEOZH5BBhnMHHb7hX8wPucI9srOw3GkBZBATxlaE5DGWP+AFn6uITZMiHZdhGXPuX6YxRr4dipYLV3hD98AlV613qYAY+seweUVfeQ6VJpVeSYmqHMyeQQEDPwqT9QJ5vKE6tWbOW95wGmGgR4D7I+NCEnV9rHoJfO1vjYdJDHzBv2akR5nY/EAWI0yXDt1wdSr1p31t/myCi7UDoknmD5Iq7MSEdzCOzP+J7LGP9Hb4EW1sjgZs5yuY42hzdfZ0QgeK8afeEIu84omuzqn0+icWc6Gkx3/rtBRJbnAGL3rOoV6MISqE5PshiVWzBflt4qPmxcn9S64ru6R9mcixn1MiKG5anG/Q="
+ }
+ ],
+ "returnValue": [
+ {
+ "type": "android.content.SharedPreferences$Editor",
+ "value": ""
+ }
+ ]
+}
+{
+ "id": "53480382-6252-49e5-8b1a-bbe7cc0540f2",
+ "category": "STORAGE",
+ "time": "2025-09-22T20:53:10.230Z",
+ "class": "androidx.security.crypto.EncryptedSharedPreferences$Editor",
+ "method": "putStringSet",
+ "stackTrace": [
+ "androidx.security.crypto.EncryptedSharedPreferences$Editor.putStringSet(Native Method)",
+ "org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:34)",
+ "org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$6$lambda$5(MainActivity.kt:55)",
+ "org.owasp.mastestapp.MainActivityKt.$r8$lambda$PtKdgqcXvbS9cMNZVWq3K3GGQKQ(Unknown Source:0)",
+ "org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0)",
+ "java.lang.Thread.run(Thread.java:1012)"
+ ],
+ "inputParameters": [
+ {
+ "type": "java.lang.String",
+ "value": "preSharedKeys"
+ },
+ {
+ "type": "java.util.Set",
+ "value": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALfX7kbfFv3pc3JjOHQ=\n-----END PRIVATE KEY-----,-----BEGIN PRIVATE KEY-----\ngJXS9EwpuzK8U1TOgfplwfKEVngCE2D5FNBQWvNmuHHbigmTCabsA=\n-----END PRIVATE KEY-----"
+ }
+ ],
+ "returnValue": [
+ {
+ "type": "android.content.SharedPreferences$Editor",
+ "value": ""
+ }
+ ]
+}
diff --git a/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/run.sh b/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/run.sh
new file mode 100755
index 00000000000..3b78c35a81e
--- /dev/null
+++ b/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/run.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+../../../../utils/frida/android/run.sh ./hooks.js