WooCommerce is the most popular e-commerce plugin for WordPress with over 5 million installations. A flaw in the way WooCommerce handles imports of products results in a stored cross-site scripting vulnerability (XSS) that can be exploited through cross-site request forgery (CSRF).
In WooCommerce shop managers and administrators have the ability to import (insert/update) products via a
.csv file. Every product in WooCommerce has a product description where the shop manager can insert limited HTML, i.e. very basic HTML tags and attributes, such as the
<a> tag in combination with the
href attribute. It is important to mention that the administrator is able to use unfiltered HTML in the WordPress default installation.
An attacker can use CSRF to import (insert/update) any product via a
.csv file. The attacker needs to upload a
.csv file which is possible with a user of the role author or higher. If the attacker tricks an administrator of a targeted blog into visiting a malicious website set up by the attacker he can import products with unsanitized HTML in the product description via CSRF. Finally, this leads to a stored XSS in every product of the vulnerable shop.
The importer functionality consists of 4 steps which are processed in the given order:
- Upload a CSV file (upload)
- Column mapping (mapping)
- Import (import)
- Done! (done)
The words in the parentheses are used as function name in the WooCommerce product importer.
The importer of WooCommerce uses the PHP function
call_user_func() to call the different steps of the importing process. The first step of the importer (upload) is protected by a nonce (anti-CSRF token), however, the other steps are not protected.
The following code snippet shows the invokation of
$this->steps is a whitelist and consists of the different importer steps described above. The attacker controlled variable is
$this->step, this means we can only call functions listed in the
view field from an
$this) object. However, we can skip the upload step of the importer and go directly to the import() function from the import step.
import() function localizes and enqueues the
wc_product_import_params variable are controlled by an attacker. Furthermore, a valid
import_nonce is created with the
wp_create_nonce() function in line 407 for the
$_POST variable and the valid nonce.
The invoked AJAX request calls the
do_ajax_product_import() function. In line 202 the nonce check of the
check_ajax_referer() function is bypassed via the self-created nonce described above. In line 204 the code checks if the current user has the privileges to import products. This is the case because the AJAX request is invoked by the victim’s browser (administrator). All used parameters like
$_POST['file'] are provided by the
wp_localize_script() described above. Finally, the products from the malicious
.csv file are imported with the XSS payload in the product description.
|2019/05/29||First contact with vendor|
|2019/05/29||Response of vendor|
|2019/06/27||Insufficient patch proposed|
|2019/06/29||Bypass #1 reported and acknowledged|
|2019/07/01||Vendor proposed a valid fix|
|2019/07/02||Fix with version 3.6.5 released|
.php files of themes and plugins from within the admin dashboard. By abusing the XSS vulnerability, the attacker can gain arbitrary PHP code execution on the remote server.