Skip to content

MASTG-DEMO-0020: Data Exclusion using backup_rules.xml with Backup Manager

Content in BETA

This content is in beta and still under active development, so it is subject to change any time (e.g. structure, IDs, content, URLs, etc.).

Send Feedback

Download MASTG-DEMO-0020 APK Open MASTG-DEMO-0020 Folder Build MASTG-DEMO-0020 APK

Sample

The following samples contain:

  • the Kotlin code that creates two files inside filesDir.
  • 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 <exclude> element.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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 internalStorageDir = context.filesDir

        val fileName = File(internalStorageDir, "secret.txt")
        val fileNameOfBackupExcludedFile = File(internalStorageDir, "backup_excluded_secret.txt")
        val fileContent = "secr3tPa\$\$W0rd\n"

        try {
            FileOutputStream(fileName).use { output ->
                output.write(fileContent.toByteArray())
                Log.d("WriteInternalStorage", "File written to internal storage successfully.")
            }
            FileOutputStream(fileNameOfBackupExcludedFile).use { output ->
                output.write(fileContent.toByteArray())
                Log.d("WriteInternalStorage", "File written to internal storage successfully.")
            }
        } catch (e: IOException) {
            Log.e("WriteInternalStorage", "Error writing file to internal storage", e)
            return "ERROR!!\n\nError writing file to internal storage"
        }

        return "SUCCESS!!\n\nFiles saved to $internalStorageDir"
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <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: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>
1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
    <include domain="file" path="." requireFlags="clientSideEncryption" />
    <exclude domain="file" path="backup_excluded_secret.txt" />
</full-backup-content>

Steps

  1. Install the target app on your device.
  2. Open the app and exercise it to trigger file creations.
  3. Execute run.sh.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/bin/bash

package_name="org.owasp.mastestapp"

adb root
adb shell "find /data/user/0/$package_name/files -type f" > output_before.txt

../../../../utils/mastg-android-backup-bmgr.sh $package_name

adb shell "find /data/user/0/$package_name/files -type f" > output_after.txt

mkdir -p restored_files
while read -r line; do
  adb pull "$line" ./restored_files/
done < output_after.txt
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/bin/bash

# Default package name
if [ -z "$1" ]; then
    echo "No package name provided. Usage: $0 <package_name>"
    exit 1

else
    package_name="$1"
fi

# 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 "$package_name" | grep -F "Package $package_name 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 "$package_name")
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 "$package_name"
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"

For simplicity, in run.sh we restrict the files to the filesDir directory (/data/user/0/org.owasp.mastestapp/files/ which is equivalent to /data/data/org.owasp.mastestapp/files/).

The run.sh script does the following:

  1. Takes a snapshot of the app data before the backup.
  2. Runs the backup script, which:
    • backs up the app data.
    • uninstalls the app.
    • restores the app data.
  3. Takes a snapshot of the app data after the restore.
  4. Retrieves the list of restored files from the device.

Observation

The output contains:

  • output.txt: the output of the run.sh script.
  • output_before.txt: the list of files before the backup.
  • output_after.txt: the list of files after the restore.
  • restored_files/: the directory containing the restored files.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
adbd is already running as root
Backup Manager now enabled
Package org.owasp.mastestapp with result: Success
/data/app/~~Es7NACiEKrtQIzZpqtLPzw==/org.owasp.mastestapp-GvkjkmAqRCTmzufVHw8YNg==/base.apk: 1 file pulled, 0 skipped. 165.5 MB/s (25924184 bytes in 0.149s)
Success
Success
Selected transport com.google.android.gms/.backup.BackupTransportService (formerly com.android.localtransport/.LocalTransport)
Done
/data/user/0/org.owasp.mastestapp/files/profileInstalled: 1 file pulled, 0 skipped. 0.0 MB/s (24 bytes in 0.001s)
/data/user/0/org.owasp.mastestapp/files/secret.txt: 1 file pulled, 0 skipped. 0.0 MB/s (15 bytes in 0.001s)
1
2
3
/data/user/0/org.owasp.mastestapp/files/secret.txt
/data/user/0/org.owasp.mastestapp/files/backup_excluded_secret.txt
/data/user/0/org.owasp.mastestapp/files/profileInstalled
1
2
/data/user/0/org.owasp.mastestapp/files/profileInstalled
/data/user/0/org.owasp.mastestapp/files/secret.txt

Evaluation

The test fails because secret.txt is restored from the backup and it contains sensitive data.

restored_files/secret.txt
1
secr3tPa$$W0rd

Note that output_after.txt does not contain the backup_excluded_secret.txt file, which is expected as it was marked as exclude in the backup_rules.xml file.