19 Jun

Authenticated Local File Inclusion (LFI) Vulnerability in ChimpMate

In seeking to continue to improve our Plugin Security Checker, which does automated checks to try spot potential security issues in WordPress plugins, we log the results of checks of plugins in the Plugin Directory. The plugin ChimpMate was recently run through that and one of the issues identified in that was a possible local file inclusion vulnerability:

Since the check for that issue is based on a fairly limited number of previously identified vulnerabilities we decided to check on that to see if what was flagged was correctly identified.

For someone with the necessary knowledge to review possible security issues more closely they can use the Developer Mode of the tool, which is available to customers of our service, to see the details of what lead to the tool warning of the possible issue. What was shown was this:

In both of the instances shown there, user input is used to include a file through the function include_once(), in the first instance the user input is directly used and in the second it used through a variable. Those both would be accurate identifications of a possible issue.

When then decided to take a look to see if there was a vulnerability caused by either of those.

The first instance was located in the function preview() in the file /admin/class-chimpmate-wpmc-assistant-admin.php and looked like this:

219
220
221
222
223
public function preview(){
	if(!isset($_GET['type']) || !isset($_GET['theme']))die();
	include_once( 'includes/'.$_GET['type'].$_GET['theme'].'.php' );
	die();
}

That code simply checks if the GET inputs “type” and “theme” exist and if they do, it uses those with the include_once() function.

That function is accessible to anyone logged in to WordPress through its AJAX functionally:

52
add_action('wp_ajax_chimpmate_prev', array( $this, 'preview' ) );

That means there is authenticated local file inclusion vulnerability, which could allow an attacker who is able to log in to WordPress to cause any .php file on the website to be run. Through cross-site request forgery (CSRF) it would also be possible for an attacker to cause anyone logged in to WordPress do the same thing.

The second instance identified by the tool could be exploited in the same way.

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

The following proof of concept will cause a file named test.php in the root directory of the WordPress installation to be included, when logged in to WordPress.

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

http://[path to WordPress]/wp-admin/admin-ajax.php?action=chimpmate_prev&type=../../../../../&theme=test

Timeline

  • June 11,2018 – Developer notified.
12 Jun

Privilege Escalation Vulnerability in Quttera Web Malware Scanner

One of the big problems we see in trying to improve security is that so often security companies are promoting product and services that they claim will protect websites, but really only try to deal with the after effects of them being hacked. What seems like could explain a lot of that is that most of those companies don’t know or care about security and they are just trying to make a buck with little to no concern whether they are providing anything of value in exchange for that money. One of the things that seems to back that up is how often security companies fail to handle basic security when it comes to their own websites and product/services.

The latest example of that was something we ran across while discussing an example of security companies’ frequent misleading to outright false claims made about their products and services. As discussed over at our main blog the makers of the plugin Quttera Web Malware Scanner had recently claimed that the plugin had over 400,000 installations despite it actually only having 10,000+ active install according to wordpress.org. After running across that we started to take a quick look at the plugin’s security and immediately found it was failing to take some basic security measures.

The plugin makes a number of functions available through WordPress’ AJAX functionality to anyone logged in to WordPress:

42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
add_action( 'wp_ajax_scanner-run_scan', 'CQtrAjaxHandler::RunExternalScan' );
 
 
/*
 * setup action @scanner-run_internal_scan mapped to callback qtr_wm_scanner_ajax_run_internal_scan
 * wp_ajax_ prefix used only for logged in users
 */ 
add_action( 'wp_ajax_scanner-run_internal_scan', 'CQtrAjaxHandler::RunInternalScan' );
 
add_action( 'wp_ajax_scanner-is_internal_scan_running', 'CQtrAjaxHandler::IsInternalScanNowRunning' );
 
add_action( 'wp_ajax_scanner-get_log_lines', 'CQtrAjaxHandler::GetLogLines' );
 
add_action( 'wp_ajax_scanner-clean_log', 'CQtrAjaxHandler::CleanLogLines' );
 
add_action( 'wp_ajax_scanner-get_stats', 'CQtrAjaxHandler::GetStats' );
 
add_action( 'wp_ajax_scanner-stop_internal_scan', 'CQtrAjaxHandler::StopInternalScan' );
 
add_action( 'wp_ajax_scanner-get_detected_threats', 'CQtrAjaxHandler::GetDetectedThreatsReport' );
 
add_action( 'wp_ajax_scanner-get_ignored_threats', 'CQtrAjaxHandler::GetIgnoredThreatsReport' );
 
add_action( 'wp_ajax_scanner-ignore_threat', 'CQtrAjaxHandler::IgnoreThreat' );
 
add_action( 'wp_ajax_scanner-get_file_report', 'CQtrAjaxHandler::ScannerReport' );
 
/* 
 * return threat back to report
 */
add_action( 'wp_ajax_scanner-unignore_threat', 'CQtrAjaxHandler::RemoveFromIgnoreList' );
 
add_action( 'wp_ajax_scanner-clean_ignore_list', 'CQtrAjaxHandler::CleanIgnoreList');
 
add_action( 'wp_ajax_scanner-whitelist_threat','CQtrAjaxHandler::WhiteListThreat' );
 
add_action( 'wp_ajax_scanner-clean_threats_whitelist', 'CQtrAjaxHandler::CleanThreatsWhiteList');
 
add_action( 'wp_ajax_scanner-whitelist_file', 'CQtrAjaxHandler::WhiteListFile');
 
add_action( 'wp_ajax_scanner-clean_files_whitelist', 'CQtrAjaxHandler::CleanFilesWhiteList');

The plugin’s admin pages, where at least most of those are intended to be accessed from, is only accessible to those with the “activate_plugins” capability, which would normally be only Administrator-level users. So there should be a check to make sure that users requesting those have that capability.

The first of the functions RunExternalScan() was restricted to those with the “manage_options” capability, which is also normally a capability only Administrator-level users have:

32
33
34
35
36
37
public static function RunExternalScan()
{
	if(!current_user_can('manage_options'))
	{
		wp_die(__('You do not have sufficient permissions to access this page.') );
	}

But that was missing from other functions. For example the GetLogLines() contains no check, so anyone logged in to WordPress could access it:

237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
public static function GetLogLines()
{
	$index  = 0;
	$logger = new CQtrLogger();
 
	if( isset( $_GET['start_line']) ) 
	{
		$index = intval( $_GET['start_line']);
	}
 
	else if( isset( $_POST['start_line']) ) 
	{
		$index = intval( $_POST['start_line']);
	}
 
 
	// $lines = $logger->GetFromLine($index);
	$lines = $logger->GetAllLines();
	echo json_encode($lines);
	exit();
}

In that case of that function, it would lead to a full path disclosure since it will display the full path to files that have been scanned.

What also was missing in all the functions we looked at was protection against cross-site request forgery (CSRF), so an attacker could cause someone logged in to WordPress to access the various functions without intending it.

After we notified the developer they released version 3.0.9.1, which partially resolves this.

The new version introduces a function __can_access() that ends the running of the code using exit() if the user doesn’t have the “manage_options” capability:

32
33
34
35
36
private static function __can_access(){
	if(!current_user_can('manage_options')){
		wp_die(__('You do not have sufficient permissions to access this page.') );
	}
}

That function is called first when those AJAX accessible functions mentioned above are run. Here, for example, is that with GetLogLines():

231
232
233
public static function GetLogLines()
{
	self::__can_access();

There still is a lack of protection against CSRF.

Proof of Concept

The following proof of concept will show the last few log lines from the plugin’s scanner, when logged in to WordPress.

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

http://[path to WordPress]/wp-admin/admin-ajax.php?action=scanner-get_log_lines

Timeline

  • June 7, 2018 – Developer notified.
  • June 7, 2018 – Developer responds.
  • June 8, 2018 – Version 3.0.9.1 released, which partially fixes the issue.
25 May

Our Proactive Monitoring Caught a PHP Object Injection Vulnerability in WordPress Survey & Poll

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 WordPress Survey & Poll the value of a cookie, “wp_sap”, was passed through the unserialize() function in several locations, which could lead to PHP object injection. One of those locations was in the function enqueue_custom_scripts_and_styles() in the file /wordpress-survey-and-poll.php:

208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
function enqueue_custom_scripts_and_styles() {
	global $wpdb;
	wp_enqueue_style( 'wp_sap_style', plugins_url( '/templates/assets/css/wp_sap.css', __FILE__ ) );
	wp_enqueue_style( 'jquery_ui_style', plugins_url( '/templates/assets/css/jquery-ui.css', __FILE__ ) );
	wp_enqueue_script( 'jquery' );
	wp_enqueue_script( 'jquery-ui-core', array( 'jquery' ) );
	wp_enqueue_script( 'jquery-effects-core', array( 'jquery' ) );
	wp_enqueue_script( 'jquery-effects-slide', array( 'jquery-effects-core' ) );
	wp_enqueue_script( 'jquery-visible',plugins_url( '/templates/assets/js/jquery.visible.min.js', __FILE__ ), array( 'jquery' ), '1.10.2' );
	wp_register_script('wp_sap_script', plugins_url( '/templates/assets/js/wp_sap.js' , __FILE__ ), array( 'jquery' ), '1.0.0.2', true );
		$survey_viewed = array();
		$sv = '';
		$sv_condition = '';
			if ( isset( $_COOKIE[ 'wp_sap' ] ) ) {
				$survey_viewed = unserialize( stripslashes( $_COOKIE[ 'wp_sap' ] ) );

That function gets called during init if the page being requested is not an admin page of WordPress and if the GET or POST input “sspcmd” exists:

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
if ( is_admin() ) {
	require_once( sprintf( "%s/settings.php", dirname( __FILE__ ) ) );
	$wp_sap_settings = new wp_sap_settings();
	$plugin = plugin_basename( __FILE__ );
	add_filter( "plugin_action_links_$plugin", array( &$this, 'plugin_settings_link' ) );
}
else {
	$wp_sap_url = $_SERVER[ 'HTTP_HOST' ] . $_SERVER[ 'REQUEST_URI' ];
	$wp_sap_load = true;
	if ( ( strpos( $wp_sap_url, 'wp-login' ) ) !== false ) {
		$wp_sap_load = false;
	}
	if ( ( strpos( $wp_sap_url, 'wp-admin' ) ) !== false ) {
		$wp_sap_load = false;
	}
	if ( $wp_sap_load || isset( $_REQUEST[ 'sspcmd' ] ) ) {
		//integrate the public functions
		add_action( 'init', array( &$this, 'enqueue_custom_scripts_and_styles' ) );

That made the vulnerable code accessible to anyone.

We notified the developer of the issue yesterday and a couple of hours later they released version 1.5.6, which resolves the vulnerability by replacing usage of unserialize() with and json_decode() (along with related usage of serialize() with json_encode()).

Proof of Concept

With our plugin for testing for PHP object injection installed and activated, set the value of the cookie “wp_sap” to “O:20:”php_object_injection”:0:{}” and then when you visit the following URL the message “PHP object injection has occurred.” will be shown.

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

http://[path to WordPress]/?sspcmd=test

Timeline

  • May 24, 2018 – Developer notified.
  • Mary 24, 2018 – Developer responds.
  • May 24, 2018 – Version 1.5.6 released, which fixes vulnerability.
21 May

Our Plugin Security Checker Found a 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 when we ran the plugin WP Google Map Plugin through the tool to check to see if it would have spotted a recently fixed reflected cross-site scripting (XSS) vulnerability in the plugin we found that the plugin still contained another vulnerability of the same type (it also would have identified the possibility of the previous vulnerability if it had been checked).

In the file /core/class.initiate-core.php the function fc_geocoding() outputs the value of the variable $_POST, which contains any POST inputs sent with a request, without escaping that:

32
33
34
35
function fc_geocoding() {
	print_r($_POST);
	exit;
}

That would lead to a reflected cross-site (XSS) vulnerability depending on if and how it can be accessed. The function is registered to be accessible through WordPress’ AJAX functionality to anyone logged in to WordPress:

23
add_action( 'wp_ajax_fc_geocoding',array( $this, 'fc_geocoding' ) );

So it would be exploitable, though that isn’t a type of vulnerability that hackers are likely to exploit on the average website and therefore there isn’t a lot of risk due to it.

We notified the developer of the issue a week ago. We haven’t heard back from them (other than an automated response that they received our form submission) 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

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

Timeline

  • May 14, 2018 – Developer notified.
18 May

When Will WordPress Finally Understand That Burying Heads in The Sand Doesn’t Improve Security?

When it comes to improving the security of WordPress plugins what continues to amaze us is the extent that people that should be part of the solution are instead part of the problem. We got a reminder of that not too long ago with a question on the wordpress.org Support Forum about a possible security issue in the plugin WP Booking.

On February 5 someone created a new topic in the support forum for the plugin with the following message:

Hi,

i found this vulnerability info –> https://php-grinder.com/vulns/view/6726087

Is this a relevant problem for me (plugin user)?
–> Vulnerability: #6726087 (2017-11-24 16:01:31)

Best Regards
Frank

That led to this response from one of the forum moderators:

@fraver I’ve archived your topic. Do not disclose vulnerabilities that way here. Please contact the plugins team with the details via plugins@wordpress.org.

There are a number of problems with that.

First, that person wasn’t disclosing a vulnerability, they were linking to another web page. If what they linked to had actually been a disclosure of a vulnerability, then it would have already been disclosed. We would say that the moderator doesn’t understand the basics of how the Internet works, but it turns out that position isn’t only theirs. In the current version of the page on reporting vulnerabilities in plugins it states that:

Even if there’s a report filed on one of the official security tracking sites, bringing more awareness to the security issue tends to increase people being hacked, and rarely speeds up the fixing.

That is downright bizarre. We are not sure what “official security tracking sites” is supposed to refer to, official in what capacity? Assuming they are referring to something like CVE, hackers can easily search for vulnerabilities there. The idea that mentioning something from one of those on the Support Forum is going to have much negative impact is something that we highly doubt somebody could provide any evidence to back up.

A further problem in this situation is that suggestion from the moderator contradicts another of the reporting instructions, as it states:

It greatly helps if you can provide us with how you verified this is an exploit (links to the plugin listing on sites like secunia.com are perfect).

If the moderator had followed the link they would have seen this message:

The person asking about that pretty clearly didn’t have any idea if there was actually a vulnerability, so if they reported this to the Plugin Directory then nothing would likely to be done. For several weeks after that, nothing happened.

What removing the message does is to limit the ability for those more knowledgeable to check on the claim. We only had access to those posts because we have an email alert set that picked them up (a hacker could do the same thing, making the deletion all the more questionable). Due to the fact that we able to see them, we were able to look further into this and confirm that there was a vulnerability.

The original claim indicates that the function get() in the file /wp-booking-management-system/shinetheme/libraries/input.php returns the value of user input without it being sanitized. Depending on where that is then is used it could lead various vulnerabilities. We did a quick look at the plugin to see if we could find an example of it leading to a vulnerability and found that in the file /shinetheme/views/admin/taxonomy/edit.php it was output without being escaped, which could leading to reflected cross-site scripting (XSS) on line 100:

<input type="search" name="keyword" value="<?php echo WPBooking_Input::get('keyword') ?>" placeholder="<?php echo esc_html__('ID','wpbooking') ?>">

We notified the developer of all of this on February 14th and then on February 27th they released version 1.5, which fixes the vulnerability. That was done by escaping the value using esc_attr():

<input type="search" name="keyword" value="<?php echo esc_attr(WPBooking_Input::get('keyword')) ?>" placeholder="<?php echo esc_html__('ID','wpbooking') ?>">

The same escaping was added to another instance of usage of that function in a different file.

Proof of Concept

The following proof of concept will cause any available cookies to be shown in alert box, when logged in 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=wpbooking_page_orders&wpbooking_apply_changes=Apply&order_service_type&status&payment_method&keyword="><script>alert(document.cookie)%3B<%2Fscript>

Timeline

  • February 14, 2018 – Developer notified.
  • February 27, 2018 – Version 1.5 released, which fixes vulnerability.
17 May

Reflected Cross-Site Scripting (XSS) Vulnerability in CF7 Invisible reCAPTCHA

In the monitoring we do to keep track of vulnerabilities in WordPress plugins for this service one thing we have noticed is that developers are not always providing full or consistent information on new version of plugins. For version 1.3.1 of the plugin CF7 Invisible reCAPTCHA the changelog entry is “Minor bug fix: Resolved the caching issue.”. The development log entry for that version indicates something different, “Security Update in Cf7 Invisible reCAPTCHA”. In looking over the new version to see if there was a vulnerability being fixed in that version what we saw was there was a significant amount of changes that were made, which seems out of line with the changelog entry description of the change being made.

Due to the amount of changes it makes it a bit hard to figure out if there was a vulnerability fixed and we didn’t find something in our look over it. But we did see a reflected cross-site scripting (XSS) vulnerability that was introduced in that version.

At the beginning of the function that generates the plugin’s admin page, vsz_cf7_invisible_recaptcha_page(), which is located in the file /cf7-Invisible-recaptcha.php, the new version added code to set the value of the GET input “tab” to the variable $tab without sanitizing or validating it:

54
55
function vsz_cf7_invisible_recaptcha_page(){
	$tab = isset($_GET["tab"]) ? $_GET["tab"] : "settings";

Further into the function the value is output without being escaped, which would permit reflected cross-site scripting (XSS) to occur:

277
jQuery("#<?php echo $tab; ?>").show();

We have yet to hear back from the developer since we notified them of the issue, but yesterday they released version 1.3.2, which fixes the vulnerability by sanitizing the input when setting it to the value of $tab:

55
$tab = isset($_GET["tab"]) ? sanitize_text_field($_GET["tab"]) : "settings";

And escaping it when being output:

277
jQuery("#<?php echo esc_attr($tab); ?>").show();

After running across this vulnerability we updated our Plugin Security Checker (which is now accessible through a WordPress plugin of its own) to detect vulnerabilities using code similar to this one.

Proof of Concept

The following proof of concept will cause any available cookies to be shown in alert box, when logged in 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=cf7-Invisible-recaptcha&tab=</script><script>alert(document.cookie);</script>

Timeline

  • May 14, 2018 – Developer notified.
  • May 16, 2018 – Version 1.3.2 released, which fixes vulnerability.
16 May

Our Proactive Monitoring Caught a Newly Introduced Arbitrary File Upload Vulnerability in a Plugin with 50,000+ Active Installations

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 in a fairly popular plugin, 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 now 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).

In the plugin KingComposer, which has 50,000+ active installations according to wordpress.org, version 2.7 introduced functionality for uploading extensions. That functionality is accessible to anyone, even those without access to admin page that is intended to be initiated from. That currently allows uploading arbitrary files, including malicious files, if the Extensions admin page of the plugin has ever been visited prior to the attempted exploitation.

The plugin runs the function init_first() whenever WordPress loads:

205
add_action( 'init', array( &$this, 'init_first' ), 0 );

That function, which is located in the file /kingcomposer.php, will cause the file /includes/kc.extensions.php to be included:

227
228
229
230
231
232
233
234
235
236
237
238
239
240
public function init_first(){
	/*
	*	Register maps
	*/
	require_once KC_PATH.'/includes/kc.maps.php';
	/*
	*	Register params
	*/
	require_once KC_PATH.'/includes/kc.param.types.php';
	/*
	*	This init action has highest priority
	*/
	require_once KC_PATH.'/includes/kc.extensions.php';
}

That file will cause a new instance of the class kc_extensions to be created:

380
new kc_extensions();

The construct function in that will run the function process_bulk_action() if is_admin() is true:

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
function __construct(){
 
	$this->path = untrailingslashit(ABSPATH).KDS.'wp-content'.KDS.'uploads'.KDS.'kc_extensions'.KDS;
 
	$this->scheme = is_ssl() ? 'https' : 'http';
	$this->api_url = $this->scheme.'://extensions.kingcomposer.com/';
 
	if (is_admin()) {
 
		add_action ('admin_menu', array( &$this, 'admin_menu' ), 1);
		if (isset($_GET['tab']) && !empty($_GET['tab']))
			$this-&gt;tab = $_GET['tab'];
		if (isset($_GET['page']) && !empty($_GET['page']))
			$this-&gt;page = $_GET['page'];
 
		add_action('kc_list_extensions_store', array(&$this, 'extensions_store'));
		add_action('kc_list_extensions_installed', array(&$this, 'extensions_installed'));
		add_action('kc_list_extensions_upload', array(&$this, 'extensions_upload'));
 
		$this->process_bulk_action();

is_admin() will return true if an admin page is being requested and can be true even if the request is coming from someone that is not logged in.

The function process_bulk_action() will run different code depending of the value of the POST input “action” specified:

248
249
250
251
252
253
254
255
256
257
public function process_bulk_action() {
 
	if( isset($_POST['action']) ){
 
		$actives = (array) get_option( 'kc_active_extensions', array() );
 
		$checked = isset($_POST['checked']) ? (array) $_POST['checked'] : array();
		$path = untrailingslashit(ABSPATH).KDS.'wp-content'.KDS.'uploads'.KDS.'kc_extensions'.KDS;
 
		switch ($_POST['action']){

If that input is set to “upload” the following code will run:

291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
case 'upload' : 
 
	$this->tab = 'upload';
 
	if (!class_exists('ZipArchive')) {
		$this->errors[] = 'Server does not support ZipArchive';
	} else if (
		(
			($_FILES["extensionzip"]["type"] == "application/zip") || 
			($_FILES["extensionzip"]["type"] == "application/x-zip") || 
			($_FILES["extensionzip"]["type"] == "application/x-zip-compressed")
		) && 
		($_FILES["extensionzip"]["size"] < 20000000) ) { if (move_uploaded_file($_FILES['extensionzip']['tmp_name'], $path.$_FILES['extensionzip']['name']) === true) { $zip = new ZipArchive; $res = $zip->open($path.$_FILES['extensionzip']['name']);
			if ($res === TRUE) {
 
				$ext = $zip->extractTo($path);

That code will unzip the contents of zip file and place them in the directory /wp-content/uploads/kc_extensions/. If that directory doesn’t currently exist then the zip file and its content cannot be placed in the directory. That directory is created during the first visit to the plugin’s Extensions admin page, which can be accessed by Administrator and Editor-level users.

We notified the developer of the plugin of the issue on April 16. They responded the same day with a proposed fix for the vulnerability, which didn’t do anything to fix it. The change, which was included in version 2.7.1, replaced the following line:

48
$this->process_bulk_action();

with

48
add_action('init', array(&$this, 'process_bulk_action'));

They explained that would cause the function “process_bulk_action()” to “run after WP core”. We are not sure what was supposed to be the point of that since the starting point of this code running already ran during “init”, when that function is now set to run.

We responded twice, first explaining that code didn’t resolve the issue and then in response to a response to that reply, that version 2.7.1 didn’t fix the issue. After our second response we didn’t receive any further response from the developer and the issue still has not been fixed despite a couple of versions being released since then. 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

The following proof of concept will place the files in a specified zip file in to the directory /wp-content/uploads/kc_extensions/.

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

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/admin-post.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="action" value="upload" />
<input type="file" name="extensionzip" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • April 16, 2018 – Developer notified.
  • April 16, 2018 – Developer responds.
10 May

Information Disclosure Vulnerability in Google Drive for WordPress (wp-google-drive)

Yesterday we had a request on this website for a file that would be at /wp-content/plugins/wp-google-drive/gdrive-ajaxs.php, which is a file from the plugin Google Drive for WordPress (wp-google-drive). Just about a month ago we had provided more details on an arbitrary file deletion vulnerability in that plugin, which had been incorrectly labeled by the discoverer, Lenon Leite, as being a remote execution (RCE) vulnerability. When exploiting that vulnerability you would send a request to that particular file, but that type of vulnerability is not one that based on past experience, hackers would likely be interested in exploiting. While hackers’ level of interest in that type of vulnerability could have changed, what seems more likely that is someone was either thinking it was a RCE vulnerability, since those have been likely to be exploited in the past, or there was something else that a hacker realized was exploitable in that plugin that would be of more interest.

In looking at what else was accessible through that file we didn’t see anything that looks like it would be likely to be exploited, but we did notice another vulnerability.

In that file different code will be run depending on the value of the POST input “ajaxstype”:

11
switch($_POST['ajaxstype']){

Through that code can be run that will perform a backup:

12
13
14
15
case 'ontimebackup':
		$bkp    = new gd_take_backup('ontime_backup');
		$backup = $bkp->schedule_time_backup();
break;

The function schedule_time_backup(), which is located in the file /class/backup-class.php, will by default create a ZIP file backup of the website’s files in the directory /wp-content/backup/ with the filename based on the current time of the server using the function time(). While it would be easy enough to iterate through possible values for the name of the file and download the generated file, it turns out it is even easier than that to download the backup file, as the code utilized in the previously disclosed arbitrary file deletion vulnerability ends with a call to a function that lists the files in the directory and even provides a download link:

17
18
19
20
21
22
23
case 'del_fl_bkp':
		gd_delete_listById($_POST['id']);
		$dir = GBACKUP_PLUGIN_BACKUPFOLDER_PATH."/".$_POST['file_name'];
		@unlink( $dir );
		$dbkp = new settings_option;
		$dbkp->file_manage_list();
break;

The plugin doesn’t appear to have been supported for years, so we haven’t attempted to notify the developer of this vulnerability.

Proof of Concept

The following proof of concept will provide a link to a ZIP file containing the website’s files.

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

This request will generate the backup (make sure to replace “[path to WordPress]” with the location of WordPress):

<html>
<body>
<form action="http://localhost/wp-content/plugins/wp-google-drive/gdrive-ajaxs.php" method="POST">
<input type="hidden" name="ajaxstype" value="ontimebackup" />
<input type="submit" value="Submit" />
</form>
</body>

This request provides a link to download the backup:

<html>
<body>
<form action="http://[path to WordPress]/wp-content/plugins/wp-google-drive/gdrive-ajaxs.php" method="POST">
<input type="hidden" name="ajaxstype" value="del_fl_bkp" />
<input type="submit" value="Submit" />
</form>
</body>
04 May

Authenticated Information Disclosure Vulnerability in Page and Post Clone

The log message for version 1.1 of the plugin Page and Post Clone was “cookie exploit resolution”.  In looking at the changes made in that version to see if that was a vulnerability that we should add to our data we found that what was being fixed there was a cross-site request forgery (CSRF) vulnerability. As far we can think of, that seems of little consequence. In looking into that though we realized that the plugin did have a slightly more serious issue that we had previously also noticed in other plugins that provide the same functionality (one of the negatives of having so many WordPress plugins is that you can have the same vulnerabilities come up again and again as new plugins are introduced).

As of version 1.1 the plugin doesn’t check if the user cloning a page or post has the ability to edit the post, which could, for example, lead to a contributor-level user or author-level users gaining access to the contents of password protected posts.

Currently the only checking done in the function content_clone() is to see if the post ID of the page or post to clone is specified and for valid nonce (to prevent CSRF):

19
20
21
22
23
24
25
26
27
28
29
function content_clone(){
	global $wpdb;	
	if (! ( isset( $_GET['post']) || isset( $_POST['post'])  || ( isset($_REQUEST['action']) && 'content_clone' == $_REQUEST['action'] ) ) ) {
		wp_die('No post to duplicate has been supplied!');
	}
 
	/*
	 * Nonce verification
	 */
	if ( !isset( $_GET['clone_nonce'] ) || !wp_verify_nonce( $_GET['clone_nonce'], basename( __FILE__ ) ) )
		return;

The nonce is accessible to anyone that can the Pages and or Posts menu and had the edit_posts capability:

113
114
115
116
117
118
function content_clone_link( $actions, $post ) {
	if (current_user_can('edit_posts')) {			
		$actions['duplicate'] = '<a title="Clone!" href="' . wp_nonce_url('admin.php?action=content_clone&post=' . $post->ID, basename(__FILE__), 'clone_nonce' ) . '" rel="permalink">Clone</a>';
	}
	return $actions;
}

The code should check if the user can edit the specific post being cloned, as that would restrict them from gaining access to posts they could not otherwise access.

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 Concept

Create a password protected post as one user and then as a separate Contributor-level user click the Clone link under that post.

Timeline

  • April 27, 2018 – Developer notified.