Skip to content

MASTG-DEMO-0139: Path Traversal via Malicious ContentProvider Filename

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

Sample

The following sample app requests a file using a custom implicit intent (org.owasp.mastestapp.REQUEST_FILE) and handles the result in onActivityResult. The selected app controls the returned Intent data and the provider metadata that the app reads through ContentResolver.query.

The app reads OpenableColumns.DISPLAY_NAME from the returned ContentProvider and uses it directly as the filename for a File under filesDir/public/. A malicious app can return a content:// URI with a display name such as ../private/secret.txt, causing the victim app to write outside the intended public/ directory.

 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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package org.owasp.mastestapp

import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.provider.OpenableColumns
import android.widget.TextView
import java.io.File
import java.io.FileOutputStream

// SUMMARY: This sample demonstrates the risk of processing unsanitized data from implicit intent results.
class MastgTest(private val context: Context) {

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

        try {
            val intent = Intent(context, VulnerableActivity::class.java)
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            context.startActivity(intent)
            r.add(Status.FAIL, "Launched VulnerableActivity to demonstrate intent response handling")
        } catch (e: Exception) {
            r.add(Status.ERROR, e.toString())
        }

        return r.toJson()
    }
}

class VulnerableActivity : Activity() {

    companion object {
        private const val REQUEST_CODE_GET_CONTENT = 1337
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val privateDir = File(filesDir, "private").apply { mkdirs() }
        File(filesDir, "public").mkdirs()
        File(privateDir, "secret.txt").writeText("Original Secret Content")

        val intent = Intent("org.owasp.mastestapp.REQUEST_FILE")
        try {
            startActivityForResult(intent, REQUEST_CODE_GET_CONTENT)
        } catch (e: android.content.ActivityNotFoundException) {
            val tv = TextView(this)
            tv.textSize = 18f
            tv.setPadding(32, 32, 32, 32)
            tv.text = "No handler found for REQUEST_FILE.\nInstall an app that handles REQUEST_FILE first."
            setContentView(tv)
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == REQUEST_CODE_GET_CONTENT && resultCode == RESULT_OK) {
            data?.data?.let { uri ->
                var fileName = "temp_file"

                val cursor = contentResolver.query(uri, null, null, null, null)
                cursor?.use {
                    if (it.moveToFirst()) {
                        val nameIndex = it.getColumnIndex(OpenableColumns.DISPLAY_NAME)
                        if (nameIndex >= 0) {
                            // FAIL: [MASTG-TEST-0375] Blindly trusting the filename provided by an external app
                            fileName = it.getString(nameIndex)
                        }
                    }
                }

                val publicDir = File(filesDir, "public")
                // FAIL: [MASTG-TEST-0375] Writing to internal storage using the unsanitized filename
                val outFile = File(publicDir, fileName)
                val content = contentResolver.openInputStream(uri)?.use { it.bufferedReader().readText() } ?: "(empty)"
                FileOutputStream(outFile).use { it.write(content.toByteArray()) }

                val tv = TextView(this)
                tv.textSize = 18f
                tv.setPadding(32, 32, 32, 32)
                tv.text = "Filename: $fileName\nResolved path: ${outFile.canonicalPath}"
                setContentView(tv)
            }
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <application>
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name=".VulnerableActivity"
            android:exported="false" />
    </application>
</manifest>

Note

Attacker App Returning Malicious ContentProvider Filename provides an example attacker app that handles org.owasp.mastestapp.REQUEST_FILE and returns a content:// URI with a provider-controlled display name. It demonstrates how an attacker-controlled app can control data returned to this sample app's implicit intent result.

Steps

  1. Use Installing Apps to install the app.
  2. Make sure Frooky can connect to the app, for example via frida-server or Frida Gadget.
  3. Use Method Hooking by running run.sh with Frooky to hook the relevant API calls.
  4. Exercise the app to trigger a flow that requests data from another app through an implicit intent.
  5. Stop the script by pressing Ctrl+C and/or q to quit the Frida CLI.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
  "category": "CODE",
  "hooks": [
    {
      "class": "java.io.File",
      "method": "$init",
      "overloads": [
        {
          "args": ["java.io.File", "java.lang.String"]
        }
      ]
    },
    {
      "class": "java.io.FileOutputStream",
      "method": "$init",
      "overloads": [
        {
          "args": ["java.io.File"]
        }
      ],
      "filterEventsByStacktrace": ["org.owasp.mastestapp.VulnerableActivity"]
    }
  ]
}
1
2
3
#!/bin/bash
# SUMMARY: This script uses Frooky (MASTG-TOOL-0145) to perform dynamic instrumentation with Frida.
frooky -U -f org.owasp.mastestapp --platform android -o output.json hooks.json

Observation

The output shows runtime file API calls reached while the app handles the activity result:

  • java.io.File.$init is called from VulnerableActivity.onActivityResult with base directory /data/user/0/org.owasp.mastestapp/files and filename public.
  • java.io.File.$init is called from VulnerableActivity.onActivityResult with base directory /data/user/0/org.owasp.mastestapp/files/public and filename ../private/secret.txt.
  • java.io.FileOutputStream.$init is called from VulnerableActivity.onActivityResult with /data/user/0/org.owasp.mastestapp/files/public/../private/secret.txt.
output.json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{"type": "summary", "hooks": [{"class": "java.io.File", "method": "$init", "overloads": [{"args": ["java.io.File", "java.lang.String"]}]}, {"class": "java.io.FileOutputStream", "method": "$init", "overloads": [{"args": ["java.io.File"]}]}], "totalHooks": 2, "errors": [], "totalErrors": 0}
{"id": "9bdac563-d09f-458a-9409-9ad436ea018b", "type": "hook", "category": "CODE", "time": "2026-05-24T18:30:30.780Z", "class": "java.io.File", "method": "$init", "instanceId": 90125910, "stackTrace": ["java.io.File.<init>(Native Method)", "android.app.ContextImpl.getFilesDir(ContextImpl.java:777)", "android.content.ContextWrapper.getFilesDir(ContextWrapper.java:261)", "androidx.profileinstaller.ProfileInstaller.writeProfile(ProfileInstaller.java:562)", "androidx.profileinstaller.ProfileInstaller.writeProfile(ProfileInstaller.java:506)", "androidx.profileinstaller.ProfileInstaller.writeProfile(ProfileInstaller.java:470)", "androidx.profileinstaller.ProfileInstallerInitializer.lambda$writeInBackground$2(ProfileInstallerInitializer.java:136)", "androidx.profileinstaller.ProfileInstallerInitializer$$ExternalSyntheticLambda2.run(D8$$SyntheticClass:0)"], "inputParameters": [{"declaredType": "java.io.File", "value": "/data/user/0/org.owasp.mastestapp", "runtimeType": "java.io.File", "instanceToString": "/data/user/0/org.owasp.mastestapp"}, {"declaredType": "java.lang.String", "value": "files"}], "returnValue": [{"declaredType": "void", "value": "void"}]}
{"id": "75b08f60-1e07-4715-ae85-8228a3f0c305", "type": "hook", "category": "CODE", "time": "2026-05-24T18:30:30.783Z", "class": "java.io.File", "method": "$init", "instanceId": 144475863, "stackTrace": ["java.io.File.<init>(Native Method)", "androidx.profileinstaller.ProfileInstaller.hasAlreadyWrittenProfileForThisInstall(ProfileInstaller.java:364)", "androidx.profileinstaller.ProfileInstaller.writeProfile(ProfileInstaller.java:564)", "androidx.profileinstaller.ProfileInstaller.writeProfile(ProfileInstaller.java:506)", "androidx.profileinstaller.ProfileInstaller.writeProfile(ProfileInstaller.java:470)", "androidx.profileinstaller.ProfileInstallerInitializer.lambda$writeInBackground$2(ProfileInstallerInitializer.java:136)", "androidx.profileinstaller.ProfileInstallerInitializer$$ExternalSyntheticLambda2.run(D8$$SyntheticClass:0)", "java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)"], "inputParameters": [{"declaredType": "java.io.File", "value": "/data/user/0/org.owasp.mastestapp/files", "runtimeType": "java.io.File", "instanceToString": "/data/user/0/org.owasp.mastestapp/files"}, {"declaredType": "java.lang.String", "value": "profileinstaller_profileWrittenFor_lastUpdateTime.dat"}], "returnValue": [{"declaredType": "void", "value": "void"}]}
{"id": "02b4a09d-2d8a-4fdf-a8f3-c32daa68aeae", "type": "hook", "category": "CODE", "time": "2026-05-24T18:30:30.786Z", "class": "java.io.File", "method": "$init", "instanceId": 218438596, "stackTrace": ["java.io.File.<init>(Native Method)", "androidx.profileinstaller.ProfileInstaller.transcodeAndWrite(ProfileInstaller.java:426)", "androidx.profileinstaller.ProfileInstaller.writeProfile(ProfileInstaller.java:566)", "androidx.profileinstaller.ProfileInstaller.writeProfile(ProfileInstaller.java:506)", "androidx.profileinstaller.ProfileInstaller.writeProfile(ProfileInstaller.java:470)", "androidx.profileinstaller.ProfileInstallerInitializer.lambda$writeInBackground$2(ProfileInstallerInitializer.java:136)", "androidx.profileinstaller.ProfileInstallerInitializer$$ExternalSyntheticLambda2.run(D8$$SyntheticClass:0)", "java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)"], "inputParameters": [{"declaredType": "java.io.File", "value": "/data/misc/profiles/cur/0/org.owasp.mastestapp", "runtimeType": "java.io.File", "instanceToString": "/data/misc/profiles/cur/0/org.owasp.mastestapp"}, {"declaredType": "java.lang.String", "value": "primary.prof"}], "returnValue": [{"declaredType": "void", "value": "void"}]}
{"id": "23577be9-652d-4ff7-9bd5-3880564fb9b5", "type": "hook", "category": "CODE", "time": "2026-05-24T18:30:30.794Z", "class": "java.io.File", "method": "$init", "instanceId": 121289389, "stackTrace": ["java.io.File.<init>(Native Method)", "androidx.profileinstaller.ProfileVerifier.writeProfileVerification(ProfileVerifier.java:163)", "androidx.profileinstaller.ProfileInstaller.writeProfile(ProfileInstaller.java:568)", "androidx.profileinstaller.ProfileInstaller.writeProfile(ProfileInstaller.java:506)", "androidx.profileinstaller.ProfileInstaller.writeProfile(ProfileInstaller.java:470)", "androidx.profileinstaller.ProfileInstallerInitializer.lambda$writeInBackground$2(ProfileInstallerInitializer.java:136)", "androidx.profileinstaller.ProfileInstallerInitializer$$ExternalSyntheticLambda2.run(D8$$SyntheticClass:0)", "java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)"], "inputParameters": [{"declaredType": "java.io.File", "value": "/data/misc/profiles/ref/org.owasp.mastestapp", "runtimeType": "java.io.File", "instanceToString": "/data/misc/profiles/ref/org.owasp.mastestapp"}, {"declaredType": "java.lang.String", "value": "primary.prof"}], "returnValue": [{"declaredType": "void", "value": "void"}]}
{"id": "7f7413fb-34b5-4f08-8fc3-8a3ab2756bd5", "type": "hook", "category": "CODE", "time": "2026-05-24T18:30:30.798Z", "class": "java.io.File", "method": "$init", "instanceId": 128999906, "stackTrace": ["java.io.File.<init>(Native Method)", "androidx.profileinstaller.ProfileVerifier.writeProfileVerification(ProfileVerifier.java:170)", "androidx.profileinstaller.ProfileInstaller.writeProfile(ProfileInstaller.java:568)", "androidx.profileinstaller.ProfileInstaller.writeProfile(ProfileInstaller.java:506)", "androidx.profileinstaller.ProfileInstaller.writeProfile(ProfileInstaller.java:470)", "androidx.profileinstaller.ProfileInstallerInitializer.lambda$writeInBackground$2(ProfileInstallerInitializer.java:136)", "androidx.profileinstaller.ProfileInstallerInitializer$$ExternalSyntheticLambda2.run(D8$$SyntheticClass:0)", "java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)"], "inputParameters": [{"declaredType": "java.io.File", "value": "/data/misc/profiles/cur/0/org.owasp.mastestapp", "runtimeType": "java.io.File", "instanceToString": "/data/misc/profiles/cur/0/org.owasp.mastestapp"}, {"declaredType": "java.lang.String", "value": "primary.prof"}], "returnValue": [{"declaredType": "void", "value": "void"}]}
{"id": "7ed75fa7-484a-46c4-bb70-dc0d324be136", "type": "hook", "category": "CODE", "time": "2026-05-24T18:30:30.802Z", "class": "java.io.File", "method": "$init", "instanceId": 179878259, "stackTrace": ["java.io.File.<init>(Native Method)", "androidx.profileinstaller.ProfileVerifier.writeProfileVerification(ProfileVerifier.java:190)", "androidx.profileinstaller.ProfileInstaller.writeProfile(ProfileInstaller.java:568)", "androidx.profileinstaller.ProfileInstaller.writeProfile(ProfileInstaller.java:506)", "androidx.profileinstaller.ProfileInstaller.writeProfile(ProfileInstaller.java:470)", "androidx.profileinstaller.ProfileInstallerInitializer.lambda$writeInBackground$2(ProfileInstallerInitializer.java:136)", "androidx.profileinstaller.ProfileInstallerInitializer$$ExternalSyntheticLambda2.run(D8$$SyntheticClass:0)", "java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)"], "inputParameters": [{"declaredType": "java.io.File", "value": "/data/user/0/org.owasp.mastestapp/files", "runtimeType": "java.io.File", "instanceToString": "/data/user/0/org.owasp.mastestapp/files"}, {"declaredType": "java.lang.String", "value": "profileInstalled"}], "returnValue": [{"declaredType": "void", "value": "void"}]}
{"id": "65bfc681-383d-4d34-b130-dc963fdacb25", "type": "hook", "category": "CODE", "time": "2026-05-24T18:30:31.420Z", "class": "java.io.File", "method": "$init", "instanceId": 150078816, "stackTrace": ["java.io.File.<init>(Native Method)", "android.app.ContextImpl.getFilesDir(ContextImpl.java:777)", "android.content.ContextWrapper.getFilesDir(ContextWrapper.java:261)", "org.owasp.mastestapp.VulnerableActivity.onCreate(MastgTest.kt:40)", "android.app.Activity.performCreate(Activity.java:8051)", "android.app.Activity.performCreate(Activity.java:8031)", "android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1329)", "android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3608)"], "inputParameters": [{"declaredType": "java.io.File", "value": "/data/user/0/org.owasp.mastestapp", "runtimeType": "java.io.File", "instanceToString": "/data/user/0/org.owasp.mastestapp"}, {"declaredType": "java.lang.String", "value": "files"}], "returnValue": [{"declaredType": "void", "value": "void"}]}
{"id": "1d41c903-64ce-48f1-abc1-229e546e4dab", "type": "hook", "category": "CODE", "time": "2026-05-24T18:30:31.422Z", "class": "java.io.File", "method": "$init", "instanceId": 82819609, "stackTrace": ["java.io.File.<init>(Native Method)", "org.owasp.mastestapp.VulnerableActivity.onCreate(MastgTest.kt:40)", "android.app.Activity.performCreate(Activity.java:8051)", "android.app.Activity.performCreate(Activity.java:8031)", "android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1329)", "android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3608)", "android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3792)", "android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)"], "inputParameters": [{"declaredType": "java.io.File", "value": "/data/user/0/org.owasp.mastestapp/files", "runtimeType": "java.io.File", "instanceToString": "/data/user/0/org.owasp.mastestapp/files"}, {"declaredType": "java.lang.String", "value": "private"}], "returnValue": [{"declaredType": "void", "value": "void"}]}
{"id": "7f38b870-5949-4cb7-9ce7-88a4d00704df", "type": "hook", "category": "CODE", "time": "2026-05-24T18:30:31.424Z", "class": "java.io.File", "method": "$init", "instanceId": 219941598, "stackTrace": ["java.io.File.<init>(Native Method)", "org.owasp.mastestapp.VulnerableActivity.onCreate(MastgTest.kt:41)", "android.app.Activity.performCreate(Activity.java:8051)", "android.app.Activity.performCreate(Activity.java:8031)", "android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1329)", "android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3608)", "android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3792)", "android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)"], "inputParameters": [{"declaredType": "java.io.File", "value": "/data/user/0/org.owasp.mastestapp/files", "runtimeType": "java.io.File", "instanceToString": "/data/user/0/org.owasp.mastestapp/files"}, {"declaredType": "java.lang.String", "value": "public"}], "returnValue": [{"declaredType": "void", "value": "void"}]}
{"id": "6df70633-7522-4c9e-a6dd-65b07d16e200", "type": "hook", "category": "CODE", "time": "2026-05-24T18:30:31.425Z", "class": "java.io.File", "method": "$init", "instanceId": 99906751, "stackTrace": ["java.io.File.<init>(Native Method)", "org.owasp.mastestapp.VulnerableActivity.onCreate(MastgTest.kt:42)", "android.app.Activity.performCreate(Activity.java:8051)", "android.app.Activity.performCreate(Activity.java:8031)", "android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1329)", "android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3608)", "android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3792)", "android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)"], "inputParameters": [{"declaredType": "java.io.File", "value": "/data/user/0/org.owasp.mastestapp/files/private", "runtimeType": "java.io.File", "instanceToString": "/data/user/0/org.owasp.mastestapp/files/private"}, {"declaredType": "java.lang.String", "value": "secret.txt"}], "returnValue": [{"declaredType": "void", "value": "void"}]}
{"id": "d1e356fc-3ebd-44fc-9635-0070324bdd39", "type": "hook", "category": "CODE", "time": "2026-05-24T18:30:31.428Z", "class": "java.io.FileOutputStream", "method": "$init", "instanceId": 258086796, "stackTrace": ["java.io.FileOutputStream.<init>(Native Method)", "kotlin.io.FilesKt__FileReadWriteKt.writeText(FileReadWrite.kt:141)", "kotlin.io.FilesKt__FileReadWriteKt.writeText$default(FileReadWrite.kt:140)", "org.owasp.mastestapp.VulnerableActivity.onCreate(MastgTest.kt:42)", "android.app.Activity.performCreate(Activity.java:8051)", "android.app.Activity.performCreate(Activity.java:8031)", "android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1329)", "android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3608)"], "inputParameters": [{"declaredType": "java.io.File", "value": "/data/user/0/org.owasp.mastestapp/files/private/secret.txt", "runtimeType": "java.io.File", "instanceToString": "/data/user/0/org.owasp.mastestapp/files/private/secret.txt"}], "returnValue": [{"declaredType": "void", "value": "void"}]}
{"id": "d1f2639a-c423-42f0-86b3-b9bc0735c351", "type": "hook", "category": "CODE", "time": "2026-05-24T18:30:31.703Z", "class": "java.io.File", "method": "$init", "instanceId": 170927941, "stackTrace": ["java.io.File.<init>(Native Method)", "org.owasp.mastestapp.VulnerableActivity.onActivityResult(MastgTest.kt:65)", "android.app.Activity.dispatchActivityResult(Activity.java:8382)", "android.app.ActivityThread.deliverResults(ActivityThread.java:5294)", "android.app.ActivityThread.handleSendResult(ActivityThread.java:5340)", "android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:54)", "android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)", "android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)"], "inputParameters": [{"declaredType": "java.io.File", "value": "/data/user/0/org.owasp.mastestapp/files", "runtimeType": "java.io.File", "instanceToString": "/data/user/0/org.owasp.mastestapp/files"}, {"declaredType": "java.lang.String", "value": "public"}], "returnValue": [{"declaredType": "void", "value": "void"}]}
{"id": "0b71e2ae-293c-4497-97e6-c75269976b90", "type": "hook", "category": "CODE", "time": "2026-05-24T18:30:31.706Z", "class": "java.io.File", "method": "$init", "instanceId": 114683546, "stackTrace": ["java.io.File.<init>(Native Method)", "org.owasp.mastestapp.VulnerableActivity.onActivityResult(MastgTest.kt:67)", "android.app.Activity.dispatchActivityResult(Activity.java:8382)", "android.app.ActivityThread.deliverResults(ActivityThread.java:5294)", "android.app.ActivityThread.handleSendResult(ActivityThread.java:5340)", "android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:54)", "android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)", "android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)"], "inputParameters": [{"declaredType": "java.io.File", "value": "/data/user/0/org.owasp.mastestapp/files/public", "runtimeType": "java.io.File", "instanceToString": "/data/user/0/org.owasp.mastestapp/files/public"}, {"declaredType": "java.lang.String", "value": "../private/secret.txt"}], "returnValue": [{"declaredType": "void", "value": "void"}]}
{"id": "900eeaf4-7df1-4418-9758-fe27c27bf79b", "type": "hook", "category": "CODE", "time": "2026-05-24T18:30:31.719Z", "class": "java.io.FileOutputStream", "method": "$init", "instanceId": 77043624, "stackTrace": ["java.io.FileOutputStream.<init>(Native Method)", "org.owasp.mastestapp.VulnerableActivity.onActivityResult(MastgTest.kt:69)", "android.app.Activity.dispatchActivityResult(Activity.java:8382)", "android.app.ActivityThread.deliverResults(ActivityThread.java:5294)", "android.app.ActivityThread.handleSendResult(ActivityThread.java:5340)", "android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:54)", "android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)", "android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)"], "inputParameters": [{"declaredType": "java.io.File", "value": "/data/user/0/org.owasp.mastestapp/files/public/../private/secret.txt", "runtimeType": "java.io.File", "instanceToString": "/data/user/0/org.owasp.mastestapp/files/public/../private/secret.txt"}], "returnValue": [{"declaredType": "void", "value": "void"}]}

Evaluation

The test case fails because data returned from an external intent result reaches a file write operation without validation or sanitization.

The returned filename comes from OpenableColumns.DISPLAY_NAME, which is provider-controlled metadata obtained through ContentResolver.query. The app uses this value directly in File(publicDir, fileName) and then writes to the resulting path with FileOutputStream.

The hook output shows this untrusted value as ../private/secret.txt, which causes the destination path to become files/public/../private/secret.txt. No validation is performed before use: the app does not verify the returned URI/provider, reject path separators, normalize and check the canonical destination path, or otherwise constrain the filename to the intended public/ directory, amongst other possible validations.