Loading HuntDB...

GitHub Security Lab (GHSL) Vulnerability Report: Insufficient path validation in ReceiveExternalFilesActivity.java (GHSL-2022-060)

Low
O
ownCloud
Submitted None
Reported by atorralba

Vulnerability Details

Technical details and impact analysis

Path Traversal
The [GitHub Security Lab](https://securitylab.github.com) team has identified potential security vulnerabilities in [Owncloud Android app](https://github.com/owncloud/android). We are committed to working with you to help resolve these issues. In this report you will find everything you need to effectively coordinate a resolution of these issues with the GHSL team. If at any point you have concerns or questions about this process, please do not hesitate to reach out to us at `[email protected]` (please include `GHSL-2022-059` or `GHSL-2022-060` as a reference). If you are _NOT_ the correct point of contact for this report, please let us know! ## Summary The Owncloud Android app handles externally-provided files in the activity `ReceiveExternalFilesActivity`, where potentially malicious file paths are not properly sanitized, allowing attackers to read from and write to the application's internal storage. ## Details ### Access to arbitrary files in the app's internal storage fix bypass `ReceiveExternalFilesActivity` handles the upload of files provided by third party components in the device. The received data can be set arbitrarily by attackers, causing some functions that handle file paths to have unexpected behavior. https://hackerone.com/reports/377107 shows how that could be exploited in the past, using the `android.intent.extra.STREAM` extra to force the application to upload its internal files, like `com.owncloud.android_preferences.xml`. To fix it, the following code was added: [`ReceiveExternalFilesActivity.java:521`](https://github.com/owncloud/android/blob/73f152c242dd818b9c4de267fe072338a35ff2ba/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java#L521) ```java private void prepareStreamsToUpload() { // --snip-- for (Uri stream : mStreamsToUpload) { String streamToUpload = stream.toString(); if (streamToUpload.contains("/data") && streamToUpload.contains(getPackageName()) && !streamToUpload.contains(getCacheDir().getPath()) ) { finish(); } } } ``` This protection can be bypassed in two ways: * Using the path returned by `getCacheDir()` in the payload, e.g. `"file:///data/user/0/com.owncloud.android/cache/../shared_prefs/com.owncloud.android_preferences.xml"`. * Using a content provider URI that uses the `org.owncloud.files` provider to access the app's internal `file` folder, e.g. `"content://org.owncloud.files/files/owncloud/logs/owncloud.2022-07-25.log"`. With those payloads, the original issue can be still exploited with the same impact. ### Write of arbitrary `.txt` files in the app's internal storage Additionally, there's another insufficient path validation when uploading a plain text file that allows to write arbitrary files in the app's internal storage. When uploading a plain text file, the following code is executed, using the user-provided text at `input` to save the file: [`ReceiveExternalFilesActivity:920`](https://github.com/owncloud/android/blob/73f152c242dd818b9c4de267fe072338a35ff2ba/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java#L920) ```java private void showUploadTextDialog() { // --snip-- final TextInputEditText input = dialogView.findViewById(R.id.inputFileName); // --snip-- setFileNameFromIntent(alertDialog, input); alertDialog.setOnShowListener(dialog -> { Button button = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE); button.setOnClickListener(view -> { // --snip-- } else { fileName += ".txt"; Uri fileUri = savePlainTextToFile(fileName); mStreamsToUpload.clear(); mStreamsToUpload.add(fileUri); uploadFiles(); } inputLayout.setErrorEnabled(error != null); inputLayout.setError(error); }); }); alertDialog.show(); } ``` By reviewing `savePlainTextToFile`, it can be seen that the plain text file is momentarily saved in the app's cache, but the destination path is built using the user-provided `fileName`: [`ReceiveExternalFilesActivity:983`](https://github.com/owncloud/android/blob/73f152c242dd818b9c4de267fe072338a35ff2ba/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java#L983) ```java private Uri savePlainTextToFile(String fileName) { Uri uri = null; String content = getIntent().getStringExtra(Intent.EXTRA_TEXT); try { File tmpFile = new File(getCacheDir(), fileName); // here FileOutputStream outputStream = new FileOutputStream(tmpFile); outputStream.write(content.getBytes()); outputStream.close(); uri = Uri.fromFile(tmpFile); } catch (IOException e) { Timber.w(e, "Failed to create temp file for uploading plain text: %s", e.getMessage()); } return uri; } ``` An attacker can exploit this using a path traversal attack to write arbitrary text files into the app's internal storage or other restricted directories accessible by it. The only restriction is that the file will always have the `.txt` extension, limiting the impact. ## Remediation Validate user input before using it to construct a file path. Ideally, follow these rules: * Do not allow more than a single `.` character. * Do not allow directory separators such as `/` or `\` (depending on the file system). * Do not rely on simply replacing problematic sequences such as `../`. For example, after applying this filter to `.../...//` the resulting string would still be `../`. * Ideally use an allowlist of known good patterns. ## Resources The following PoC demonstrates how to upload arbitrary files from the app's internal storage: ``` adb shell am start -n com.owncloud.android.debug/com.owncloud.android.ui.activity.ReceiveExternalFilesActivity -t "text/plain" -a "android.intent.action.SEND" --eu "android.intent.extra.STREAM" "file:///data/user/0/com.owncloud.android.debug/cache/../shared_prefs/com.owncloud.android.debug_preferences.xml" ``` The following PoC demonstrates how to upload arbitrary files from the app's internal `files` directory: ``` adb shell am start -n com.owncloud.android.debug/com.owncloud.android.ui.activity.ReceiveExternalFilesActivity -t "text/plain" -a "android.intent.action.SEND" --eu "android.intent.extra.STREAM" "content://org.owncloud.files/files/owncloud/logs/owncloud.2022-07-25.log" ``` The following PoC demonstrates how to write an arbitrary `test.txt` text file to the app's internal storage: ``` adb shell am start -n com.owncloud.android.debug/com.owncloud.android.ui.activity.ReceiveExternalFilesActivity -t "text/plain" -a "android.intent.action.SEND" --es "android.intent.extra.TEXT" "Arbitrary contents here" --es "android.intent.extra.TITLE" "../shared_prefs/test" ``` ## GitHub Security Advisories We recommend you create a private [GitHub Security Advisory](https://help.github.com/en/github/managing-security-vulnerabilities/creating-a-security-advisory) for these findings. This also allows you to invite the GHSL team to collaborate and further discuss these findings in private before they are [published](https://help.github.com/en/github/managing-security-vulnerabilities/publishing-a-security-advisory). ## Credit These issues were discovered and reported by the CodeQL team member [@atorralba (Tony Torralba)](https://github.com/atorralba). ## Contact You can contact the GHSL team at `[email protected]`, please include a reference to `GHSL-2022-059` or `GHSL-2022-060` in any communication regarding these issues. ## Disclosure Policy This report is subject to our [coordinated disclosure policy](https://securitylab.github.com/advisories#policy). ## Impact These issues may lead to information disclosure when uploading the app's internal files, and to arbitrary file write when uploading plain text files (although limited by the `.txt` extension).

Report Details

Additional information and metadata

State

Closed

Substate

Resolved

Bounty

$50.00

Submitted

Weakness

Path Traversal