Full Disclosure of CSRF/SSRF Vulnerability in WordPress Plugin With 800,000+ Installs
One of the impediments we see to improving security of WordPress plugins (as well as security in general) is that security journalist don’t provide a good picture of what is and isn’t going on, so others don’t understand what is actually needed to be done to improve the situation. One recent example comes from Catalin Cimpanu at ZDNet’s Zero Day blog who put forward this one sided (at best) portrayal of the handling of the security of WordPress plugins by the people on the WordPress side of things:
Campbell says the WordPress team has been collaborating with the authors of the most popular plugins on its Plugins repository. It’s been helping these plugins follow best coding practices.
This has yielded great results, Campbell said, as smaller plugins have now started to follow (or steal) the coding techniques used by these larger projects, and indirectly have raised the security of their own plugins.
We had tried to provide some balance to this claim, but Mr. Cimpanu was uninterested in that. The reality of the security of plugins is that, for example, there are currently plugins with nearly 3 million installations that have publicly disclosed vulnerabilities in there current versions that are still in the Plugin Directory, which seems like a significant contrast to that picture. Making the lack of balanced coverage worse, security journalists often copy each other, so it looks like that post lead to at least one other article that provides the public without a balanced view of things.
The post provides no evidence to back up those claims and they seem hard to believe without that. For one thing we continue to see that the people on the WordPress side of things continue to fail to catch easily spotted serious vulnerabilities despite their claim to be doing a manual security review of those plugins. Considering that not only continues to happen, but they have had no interest in our continued offer to help them avoid that, it seems hard to believe that the claims are accurate.
Another point against that is that yet again a basic security failure in the plugin WP Fastest Cache has lead to a vulnerability in it being discovered. That plugin has 800,000+ active installations according to wordpress.org, which seems to us to make it one of the most popular plugins.
Back in June of 2016 we noticed that the plugin lacked protection against cross-site request forgery (CSRF) after Wordfence had missed that when disclosing related vulnerabilities, and we disclosed a vulnerability caused by that after the developer denied that CSRF vulnerabilities exist. We had notified the WordPress team of the disclosure and after they removed the plugin from the Plugin Directory that specific vulnerability was fixed, but the CSRF issues were not fixed in a more systematic fashion.
CSRF involves causing someone else to take an action they didn’t intend to.
Since then, in November of last year and earlier this month we have discussed details of other instances of vulnerabilities in the plugin related to the ongoing failure to fully protect against CSRF that had been discovered by others.
The lack of CSRF protection came up again when we went to check over the 1,000 most popular plugins to see if any of them contained a particular variant of a server-side request forgery (SSRF) vulnerability we had seen while looking into another possible security issue that popped up during our proactive monitoring of changes made to plugins in the Plugin Directory to try to catch serious vulnerabilities before they are exploited.
We found that a possible SSRF vulnerability in the WP Fastest Cache occurred in the function check_url(), which is located in the file /inc/cdn.php. That function passes user input in the form of the GET input “url” to the WordPress function wp_remote_get(), which causes a request for URL specified in the user input to made from the server, which is SSRF:
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | public static function check_url(){ if(current_user_can('manage_options')){ if(isset($_GET["type"]) && $_GET["type"] == "cloudflare"){ CdnWPFC::cloudflare_change_settings(); } if(preg_match("/wp\.com/", $_GET["url"]) || $_GET["url"] == "random"){ wp_send_json(array("success" => true)); } $host = str_replace("www.", "", $_SERVER["HTTP_HOST"]); $_GET["url"] = esc_url_raw($_GET["url"]); if(!preg_match("/^http/", $_GET["url"])){ $_GET["url"] = "http://".$_GET["url"]; } $response = wp_remote_get($_GET["url"], array('timeout' => 20 ) ); |
A check at the beginning of that restricts access to the rest of the code there to those with the “manage_options” capability, which would normally only be Administrator-level users. Those users would normally be able to do the equivalent of SSRF, so it wouldn’t be a vulnerability for them to be able to do that. That is where CSRF comes in since unless there is protection against that, someone else could cause a logged in Administrator to exploit that without intending it. In this case that could be done by simply placing a link in a blog post comment, which accesses the functionality (as shown in the proof of concept below). This type of vulnerability could be used by a more advanced attacker to send requests to addresses accessible by the server but that they couldn’t access themselves.
That code doesn’t include protection against CSRF and the code that runs before that also doesn’t. The function check_url() is called by the function wpfc_check_url_ajax_request_callback(), which is located in the file /wpFastestCache.php:
428 429 430 431 | public function wpfc_check_url_ajax_request_callback(){ include_once('inc/cdn.php'); CdnWPFC::check_url(); } |
That function in turn is accessible to anyone logged in to WordPress through WordPress’ AJAX functionality:
108 | add_action( 'wp_ajax_wpfc_check_url', array($this, 'wpfc_check_url_ajax_request_callback')); |
In looking back at older version so we could accurately tell our customers of what versions are impacted by this, we found that originally you didn’t even need to be logged in to WordPress to access this functionality. Later on it was restricted to anyone logged in to WordPress and finally it reached the current state with it accessible through CSRF.
Due to the moderators of the WordPress Support Forum’s continued inappropriate behavior we are full disclosing vulnerabilities in protest until WordPress gets that situation cleaned up, so we are releasing this post and then only trying to notify the developer through the WordPress Support Forum. Hopefully they will finally see the light and clean up their act soon, so these full disclosures will no longer be needed (we hope they end soon).
If the team handling the security of WordPress plugins really was interested in the security of plugins they should exert pressure to get that cleaned up (unfortunately though, two members of the team handling them are moderators that themselves have acted inappropriately
Proof of Concept
The following proof of concept will cause a request to be the specified URL, when logged in as an Administrator.
Make sure to replace “[path to WordPress]” with the location of WordPress and “URL” with the URL to be requested.
http://[path to WordPress]/wp-admin/admin-ajax.php?action=wpfc_check_url&url=[URL]