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 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
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¶
- Use Installing Apps to install the app.
- Make sure Frooky can connect to the app, for example via frida-server or Frida Gadget.
- Use Method Hooking by running
run.shwith Frooky to hook the relevant API calls. - Exercise the app to trigger a flow that requests data from another app through an implicit intent.
- Stop the script by pressing
Ctrl+Cand/orqto 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 | |
1 2 3 | |
Observation¶
The output shows runtime file API calls reached while the app handles the activity result:
java.io.File.$initis called fromVulnerableActivity.onActivityResultwith base directory/data/user/0/org.owasp.mastestapp/filesand filenamepublic.java.io.File.$initis called fromVulnerableActivity.onActivityResultwith base directory/data/user/0/org.owasp.mastestapp/files/publicand filename../private/secret.txt.java.io.FileOutputStream.$initis called fromVulnerableActivity.onActivityResultwith/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 | |
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.