diff --git a/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/AndroidManifest.xml b/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/AndroidManifest.xml
new file mode 100644
index 0000000000..0c2de9aefe
--- /dev/null
+++ b/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/AndroidManifest.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/MASTG-DEMO-0020.md b/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/MASTG-DEMO-0020.md
new file mode 100644
index 0000000000..1cdb378707
--- /dev/null
+++ b/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/MASTG-DEMO-0020.md
@@ -0,0 +1,43 @@
+---
+platform: android
+title: Uses of AutoBackup backup_rules.xml to Exclude Data From Backups
+id: MASTG-DEMO-0020
+code: [kotlin]
+test: MASTG-TEST-0216
+---
+
+### Sample
+
+The following samples contain:
+
+- the Kotlin code that creates two files inside [`filesDir`](https://developer.android.com/reference/android/content/Context#getFilesDir()).
+- the AndroidManifest.xml with the `android:fullBackupContent` attribute (for Android 11 and lower).
+- the `backup_rules.xml` file including a rule to exclude one of the files using an `` element.
+
+{{ MastgTest.kt # AndroidManifest.xml # backup_rules.xml }}
+
+### Steps
+
+1. Install the target app on your device.
+2. Execute `run_before.sh` which runs @MASTG-TOOL-0004.
+3. Open the app and exercise it to trigger file creations.
+4. Execute `run_after.sh`.
+5. Close the app once you finish testing.
+
+{{ run.sh }}
+
+### Observation
+
+The output contains the list of all restored files which were written to the `./restored_files/` directory:
+
+{{ output.txt }}
+
+The app wrote two files: `backup_excluded_secret.txt` which is not restored because it is marked as `exclude` in the `backup_rules.xml` file, and `secret.txt`, which contains a password:
+
+{{ restored_files/secret.txt }}
+
+Note that `/data/user/0/org.owasp.mastestapp/files/` is equivalent to `/data/data/org.owasp.mastestapp/files/`.
+
+### Evaluation
+
+The test fails because `secret.txt` is restored from the backup and it contains sensitive data.
diff --git a/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/MastgTest.kt b/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/MastgTest.kt
new file mode 100644
index 0000000000..6b0523aaee
--- /dev/null
+++ b/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/MastgTest.kt
@@ -0,0 +1,35 @@
+package org.owasp.mastestapp
+
+import android.content.Context
+import android.util.Log
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+
+class MastgTest (private val context: Context){
+
+ fun mastgTest(): String {
+
+ val externalStorageDir = context.getExternalFilesDir(null)
+
+ val fileName = File(externalStorageDir, "secret.txt")
+ val fileNameOfBackupExcludedFile = File(externalStorageDir, "backup_excluded_secret.txt")
+ val fileContent = "secr3tPa\$\$W0rd\n"
+
+ try {
+ FileOutputStream(fileName).use { output ->
+ output.write(fileContent.toByteArray())
+ Log.d("WriteExternalStorage", "File written to external storage successfully.")
+ }
+ FileOutputStream(fileNameOfBackupExcludedFile).use { output ->
+ output.write(fileContent.toByteArray())
+ Log.d("WriteExternalStorage", "File written to external storage successfully.")
+ }
+ } catch (e: IOException) {
+ Log.e("WriteExternalStorage", "Error writing file to external storage", e)
+ return "ERROR!!\n\nError writing file to external storage"
+ }
+
+ return "SUCCESS!!\n\nFiles saved to $externalStorageDir"
+ }
+}
diff --git a/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/backup_rules.xml b/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/backup_rules.xml
new file mode 100644
index 0000000000..ce8d4cd0d3
--- /dev/null
+++ b/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/backup_rules.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/output.txt b/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/output.txt
new file mode 100644
index 0000000000..1ea8748cd7
--- /dev/null
+++ b/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/output.txt
@@ -0,0 +1,2 @@
+/data/user/0/org.owasp.mastestapp/files/profileInstalled
+/data/user/0/org.owasp.mastestapp/files/secret.txt
diff --git a/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/restored_files/profileInstalled b/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/restored_files/profileInstalled
new file mode 100644
index 0000000000..afa2ee2bf6
Binary files /dev/null and b/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/restored_files/profileInstalled differ
diff --git a/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/restored_files/secret.txt b/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/restored_files/secret.txt
new file mode 100644
index 0000000000..c6c30947a8
--- /dev/null
+++ b/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/restored_files/secret.txt
@@ -0,0 +1 @@
+secr3tPa$$W0rd
diff --git a/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/run.sh b/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/run.sh
new file mode 100755
index 0000000000..2e67868c99
--- /dev/null
+++ b/demos/android/MASVS-STORAGE/MASTG-DEMO-0020/run.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+# USAGE: ./run.sh
+# EXAMPLE: ./run.sh org.owasp.mastestapp
+# SUMMARY: List all files restored from a backup
+
+# Script from https://developer.android.com/identity/data/testingbackup
+# Initialize and create a backup
+adb shell bmgr enable true
+adb shell bmgr transport com.android.localtransport/.LocalTransport | grep -q "Selected transport" || (echo "Error: error selecting local transport"; exit 1)
+adb shell settings put secure backup_local_transport_parameters 'is_encrypted=true'
+adb shell bmgr backupnow "$1" | grep -F "Package $1 with result: Success" || (echo "Backup failed"; exit 1)
+
+# Uninstall and reinstall the app to clear the data and trigger a restore
+apk_path_list=$(adb shell pm path "$1")
+OIFS=$IFS
+IFS=$'\n'
+apk_number=0
+for apk_line in $apk_path_list
+do
+ (( ++apk_number ))
+ apk_path=${apk_line:8:1000}
+ adb pull "$apk_path" "myapk${apk_number}.apk"
+done
+IFS=$OIFS
+adb shell pm uninstall --user 0 "$1"
+apks=$(seq -f 'myapk%.f.apk' 1 $apk_number)
+adb install-multiple -t --user 0 $apks
+
+# Clean up
+adb shell bmgr transport com.google.android.gms/.backup.BackupTransportService
+rm $apks
+
+echo "Done"
+
+
+# Demo script
+# You might need to enable root for ADB first with `adb root`
+adb shell "find /data/user/0/org.owasp.mastestapp/ -type f" > output.txt
+mkdir -p restored_files
+while read -r line; do
+ adb pull "$line" ./restored_files/
+done < output.txt
diff --git a/demos/ios/MASVS-STORAGE/MASTG-DEMO-0019/MASTG-DEMO-0019.md b/demos/ios/MASVS-STORAGE/MASTG-DEMO-0019/MASTG-DEMO-0019.md
new file mode 100644
index 0000000000..5cae41290b
--- /dev/null
+++ b/demos/ios/MASVS-STORAGE/MASTG-DEMO-0019/MASTG-DEMO-0019.md
@@ -0,0 +1,32 @@
+---
+platform: ios
+title: Uses of isExcludedFromBackupKey to Exclude Data From Backups
+code: [swift]
+id: MASTG-DEMO-0019
+test: MASTG-TEST-0215
+---
+
+### Sample
+
+The code snippet below shows sample code that creates a file and marks it with `isExcludedFromBackupKey`.
+
+{{ MastgTest.swift }}
+
+### Steps
+
+1. Unzip the app package and locate the main binary file (@MASTG-TECH-0058), which in this case is `./Payload/MASTestApp.app/MASTestApp`.
+2. Run `run.sh`.
+
+{{ run.sh }}
+
+### Observation
+
+The output reveals the use of `isExcludedFromBackupKey` in the app.
+
+{{ output.txt }}
+
+### Evaluation
+
+The test fails because `secret.txt` might be restored from the backup and it contains sensitive data.
+
+You can see the call to `isExcludedFromBackupKey` at `0x100004594` and the associated file, `secret.txt` at `0x10000443c`.
diff --git a/demos/ios/MASVS-STORAGE/MASTG-DEMO-0019/MASTestApp b/demos/ios/MASVS-STORAGE/MASTG-DEMO-0019/MASTestApp
new file mode 100755
index 0000000000..e2071dfa86
Binary files /dev/null and b/demos/ios/MASVS-STORAGE/MASTG-DEMO-0019/MASTestApp differ
diff --git a/demos/ios/MASVS-STORAGE/MASTG-DEMO-0019/MastgTest.swift b/demos/ios/MASVS-STORAGE/MASTG-DEMO-0019/MastgTest.swift
new file mode 100644
index 0000000000..12de2f8785
--- /dev/null
+++ b/demos/ios/MASVS-STORAGE/MASTG-DEMO-0019/MastgTest.swift
@@ -0,0 +1,27 @@
+import SwiftUI
+
+struct MastgTest {
+ static func mastgTest(completion: @escaping (String) -> Void) {
+ // Define the file name and create the file URL
+ let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
+ let fileName = "secret.txt"
+ var fileURL = documentsDirectory.appendingPathComponent(fileName)
+
+ // Create the file content
+ let fileContent = "MAS_API_KEY=8767086b9f6f976g-a8df76"
+
+ do {
+ try fileContent.write(to: fileURL, atomically: true, encoding: .utf8)
+
+ // Set the isExcludedFromBackup flag
+ var resourceValues = URLResourceValues()
+ resourceValues.isExcludedFromBackup = true
+
+ try fileURL.setResourceValues(resourceValues)
+
+ } catch {
+ completion("Error creating file: \(error)")
+ }
+ completion("File created and isExcludedFromBackup flag set to true")
+ }
+}
diff --git a/demos/ios/MASVS-STORAGE/MASTG-DEMO-0019/isExcludedFromBackup.r2 b/demos/ios/MASVS-STORAGE/MASTG-DEMO-0019/isExcludedFromBackup.r2
new file mode 100644
index 0000000000..61c735b68e
--- /dev/null
+++ b/demos/ios/MASVS-STORAGE/MASTG-DEMO-0019/isExcludedFromBackup.r2
@@ -0,0 +1,20 @@
+!printf "Uses of isExcludedFromBackup:\n"
+afl~isExcludedFromBackup
+
+!printf "\n"
+
+!printf "xrefs to isExcludedFromBackup:\n"
+axt @ 0x10000cc28
+
+!printf "\n"
+!printf "Use of isExcludedFromBackup:\n"
+
+pd-- 5 @ 0x100004594
+
+!printf "\n"
+!print "Search for secret.txt"
+/ secret.txt
+
+!printf "\n"
+!printf "Use of the string secret.txt:\n"
+pd-- 5 @ 0x10000443c
diff --git a/demos/ios/MASVS-STORAGE/MASTG-DEMO-0019/output.txt b/demos/ios/MASVS-STORAGE/MASTG-DEMO-0019/output.txt
new file mode 100644
index 0000000000..1ef7909493
--- /dev/null
+++ b/demos/ios/MASVS-STORAGE/MASTG-DEMO-0019/output.txt
@@ -0,0 +1,31 @@
+Uses of isExcludedFromBackup:
+0x10000cc28 1 12 sym.imp.Foundation.URLResourceValues.isExcludedFromBackup...Sgvs
+
+xrefs to isExcludedFromBackup:
+sym.MASTestApp.MastgTest.mastg.completion...FZ 0x100004594 [CALL:--x] bl sym.imp.Foundation.URLResourceValues.isExcludedFromBackup...Sgvs
+
+Use of isExcludedFromBackup:
+│ 0x100004580 080540f9 ldr x8, [x8, 8]
+│ 0x100004584 00013fd6 blr x8
+│ 0x100004588 e80314aa mov x8, x20
+│ 0x10000458c a1210094 bl sym.imp.Foundation.URLResourceValues...VACycfC...ycfC
+│ 0x100004590 20008052 mov w0, 1
+│ 0x100004594 a5210094 bl sym Foundation.URLResourceValues.isExcludedFromBackup...Sgvs ; sym.imp.Foundation.URLResourceValues.isExcludedFromBackup...Sgvs
+│ 0x100004598 a82300d1 sub x8, x29, 8
+│ 0x10000459c 140150f8 ldur x20, [x8, -0x100]
+│ 0x1000045a0 a82302d1 sub x8, x29, 0x88
+│ 0x1000045a4 080150f8 ldur x8, [x8, -0x100]
+
+0x10000dbe6 hit4_0 .lueFatal errorsecret.txt.
+
+Use of the string secret.txt:
+│ 0x100004428 e00314aa mov x0, x20
+│ 0x10000442c 00013fd6 blr x8
+│ 0x100004430 a08351f8 ldur x0, [x29, -0xe8] ; void *instance
+│ 0x100004434 1b220094 bl sym.imp.objc_release ; void objc_release(void *instance)
+│ 0x100004438 400000b0 adrp x0, sym.imp.swift_getObjCClassMetadata ; 0x10000d000
+│ 0x10000443c 00982f91 add x0, x0, 0xbe6 ; 0x10000dbe6 ; "secret.txt"
+│ 0x100004440 48018052 mov w8, 0xa
+│ 0x100004444 e10308aa mov x1, x8
+│ 0x100004448 28008052 mov w8, 1
+│ 0x10000444c a91303d1 sub x9, x29, 0xc4
diff --git a/demos/ios/MASVS-STORAGE/MASTG-DEMO-0019/run.sh b/demos/ios/MASVS-STORAGE/MASTG-DEMO-0019/run.sh
new file mode 100755
index 0000000000..e68eeb1088
--- /dev/null
+++ b/demos/ios/MASVS-STORAGE/MASTG-DEMO-0019/run.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+r2 -q -i isExcludedFromBackup.r2 -A MASTestApp
diff --git a/tests-beta/android/MASVS-STORAGE/MASTG-TEST-0216.md b/tests-beta/android/MASVS-STORAGE/MASTG-TEST-0216.md
new file mode 100644
index 0000000000..f8fcb221ae
--- /dev/null
+++ b/tests-beta/android/MASVS-STORAGE/MASTG-TEST-0216.md
@@ -0,0 +1,39 @@
+---
+platform: android
+title: Sensitive Data Not Excluded From Backup
+id: MASTG-TEST-0216
+type: [dynamic, filesystem]
+weakness: MASWE-0004
+---
+
+## Overview
+
+This test verifies whether your app correctly instructs the system to exclude sensitive files from backups.
+
+["Android Backups"](../../../0x05d-Testing-Data-Storage/#backups) can be implemented via [Auto Backup](https://developer.android.com/identity/data/autobackup) (Android 6.0 (API level 23) and higher) and [Key-value backup](https://developer.android.com/identity/data/keyvaluebackup) (Android 2.2 (API level 8) and higher). Auto Backup is the recommended approach by Android as it is enabled by default and requires no work to implement.
+
+To exclude specific files when using Auto Backup, developers must explicitly define exclusion rules in the `exclude` tag in `backup_rules.xml` (for Android 11 or lower using `android:fullBackupContent`) or `data_extraction_rules.xml` (for Android 12 and higher using `android:dataExtractionRules`), depending on the target API. The `cloud-backup` and `device-transfer` parameters can be used to exclude files from cloud backups and device-to-device transfers, respectively. The key-value backup approach requires developers to set up a [`BackupAgent`](https://developer.android.com/identity/data/keyvaluebackup#BackupAgent) or [`BackupAgentHelper`](https://developer.android.com/identity/data/keyvaluebackup#BackupAgentHelper) and specify what data should be backed up.
+
+Regardless of which approach the app used, Android provides a way to start the backup daemon to back up and restore app files. You can use this daemon for testing purposes and initiate the backup process and restore the app's data, allowing you to verify which files were restored from the backup.
+
+## Steps
+
+1. Start the device.
+2. Install an app on your device.
+3. Launch and use the app going through the various workflows while inputting sensitive data wherever you can.
+4. Run the backup daemon.
+5. Uninstall and reinstall the app but don't open it anymore.
+6. Restore the data from the backup and get the list of restored files.
+
+## Observation
+
+The output should contain a list of files that are restored from the backup.
+
+## Evaluation
+
+The test fails if any of the files are considered sensitive.
+
+For the sensitive files found, instruct the system to exclude them from the backup:
+
+- If you are using Auto Backup, mark them with the `exclude` tag in `backup_rules.xml` (for Android 11 or lower using `android:fullBackupContent`) or `data_extraction_rules.xml` (for Android 12 and higher using `android:dataExtractionRules`), depending on the target API. Make sure to use both the `cloud-backup` and `device-transfer` parameters.
+- If you are using the key-value approach, set up your [BackupAgent](https://developer.android.com/identity/data/keyvaluebackup#BackupAgent) accordingly.
diff --git a/tests-beta/ios/MASVS-STORAGE/MASTG-TEST-0215.md b/tests-beta/ios/MASVS-STORAGE/MASTG-TEST-0215.md
new file mode 100644
index 0000000000..a3a4ecb340
--- /dev/null
+++ b/tests-beta/ios/MASVS-STORAGE/MASTG-TEST-0215.md
@@ -0,0 +1,31 @@
+---
+platform: ios
+title: Sensitive Data Not Excluded From Backup
+id: MASTG-TEST-0215
+type: [static, filesystem]
+weakness: MASWE-0004
+---
+
+## Overview
+
+This test verifies whether your app correctly instructs the system to exclude sensitive files from backups.
+
+Files in the `/tmp` and `/Library/Caches` subdirectories of the app container are excluded from iCloud Backups. For files and directories in any other locations within the app container, iOS provides the [`isExcludedFromBackup`](https://developer.apple.com/documentation/foundation/urlresourcevalues/1780002-isexcludedfrombackup) API to guide the system not to back up a given file or directory. However, this API [does not guarantee guarantee the actual exclusion](https://developer.apple.com/documentation/foundation/optimizing_your_app_s_data_for_icloud_backup/#3928527):
+
+> "The `isExcludedFromBackup` resource value exists only to provide guidance to the system about which files and directories it can exclude; it's not a mechanism to guarantee those items never appear in a backup or on a restored device."
+
+Therefore, the only way to properly protect your files from a backup is to encrypt them.
+
+## Steps
+
+1. Run a static analysis tool such as @MASTG-TOOL-0073 on the app binary, or use a dynamic analysis tool like @MASTG-TOOL-0039, and look for uses of `isExcludedFromBackup` API.
+
+## Observation
+
+The output should contain the disassembled code of the functions using `isExcludedFromBackup` and if possible the list of affected files.
+
+## Evaluation
+
+The test case fails if you can find the use of `isExcludedFromBackup` within the source code and if any of the affected files can be considered sensitive.
+
+For the sensitive files found, and in addition to using `isExcludedFromBackup`, make sure to encrypt them, as `isExcludedFromBackup` does not guarantee the exclusion.
diff --git a/weaknesses/MASVS-STORAGE/MASWE-0004.md b/weaknesses/MASVS-STORAGE/MASWE-0004.md
index 4a3f7602dc..5e8e5af686 100644
--- a/weaknesses/MASVS-STORAGE/MASWE-0004.md
+++ b/weaknesses/MASVS-STORAGE/MASWE-0004.md
@@ -11,13 +11,28 @@ mappings:
refs:
- https://developer.android.com/guide/topics/data/autobackup#include-exclude-android-11
- https://developer.android.com/guide/topics/data/autobackup#include-exclude-android-12
-draft:
- description: sensitive data can be excluded to prevent it from being backed up.
- topics:
- - '`android:fullBackupContent` (Android 11-) or `android:dataExtractionRules` (Android
- 12+)'
- - iOS `isExcludedFromBackup` (iOS)
-status: draft
+status: new
---
+## Overview
+
+iOS and Android automatically back up app data to cloud services, and users can also create local backups on physical machines, or backups are automatically created during data transfers when switching between phones. When developers fail to properly configure how their app handles backups and neglect to exclude sensitive files, the backups may contain sensitive user and app data. Under certain conditions, the backups may not be adequately secured by the cloud provider, or a malicious actor could tamper with the backed up files, potentially altering the app's behavior or extracting confidential information.
+
+## Impact
+
+- **Modification of App's Behavior**: An attacker can tamper with data inside the backup, altering the app's logic. For example, they could modify a database that tracks the state of premium features and then restore the modified backup to the device. Another common scenario is backing up the device before redeeming a one-time coupon and restoring the backup afterward, which would allow the malicious actor to reuse the same coupon multiple times.
+- **Loss of Confidentiality**: Sensitive data stored in backups (e.g., personal information, photos, documents or audio files) may be extracted by attackers, leading to privacy breaches.
+- **Leakage of Authentication Material**: An attacker can extract passwords, cryptographic keys, and session tokens to facilitate additional attacks, such as identity theft, account takeover, or unauthorized access.
+
+## Modes of Introduction
+
+- **Automatic System Backups**: By default, iOS and Android back up app data to the cloud once the user consents during the initial setup.
+- **Local Backups**: Users can back up their devices to local systems (e.g., laptops). If local backups are stored unencrypted or not securely handled, attackers could tamper with this data.
+- **Device-To-Device Transfer**: Transferring data between devices (e.g., via iCloud or Google's device-to-device migration tools) enables an attacker to perform similar attacks.
+
+## Mitigations
+
+- Exclude sensitive files from backups using platform-specific attributes, such as `android:allowBackup` or `BackupAgent` with `excludeFromBackup` for Android. On iOS, API such as `NSURLIsExcludedFromBackupKey` [doesn't guarantee](https://developer.apple.com/documentation/foundation/optimizing_your_app_s_data_for_icloud_backup/#3928527) exclusion from the backup. Therefore, you should encrypt your data instead.
+- Store sensitive data in locations excluded from backups by default, like the Keychain or `Library/Caches` on iOS.
+- Encrypt sensitive data before storage to ensure confidentiality, even if it gets backed up.