08 Dec

It Would Probably Be a Good Idea to Be Moving Off of the Captcha WordPress Plugin

The takeover of popular WordPress plugins and then use of them for nefarious purposes has been a major issue when it comes to the security of WordPress plugins this year. Even if the takeover is not done with malicious purposes in mind, a new developer that doesn’t know what they are doing can take an otherwise relatively secure plugin and in a short time make tens or hundreds of thousands of websites insecure. At least that latter issue is true of the plugin Captcha.

The plugin Captcha has 300,000+ active installations according to WordPress.org, including this website and another of ours. Back in July the plugin was handed over from the previous developer, BestWebSoft, to another entity. Then in September an update to the plugin caused the admin area of our other website using the plugin to not function, we were not alone in that. It was only at that point that BestWebSoft mentioned that ownership had been transferred, though the new developer isn’t named:

Recently, we’ve handed over all the rights to use and manage the free version of Captcha plugin. Now, it has new owners which are responsible for the updates, troubleshooting and support any processes connected with its free version.

Going back to the commit when that change occurred, the copyright listing on files was changed from listing BestWebSoft as being the copyrighter to no one listed. The new author of the plugin is “wpdevmgr2678”, which doesn’t exactly project a professional image of the new developer.

The issue of causing the admin area to be inaccessible was then fixed. But then another update caused the admin area of this website to be inaccessible yesterday. As we started to look in to what all was going on, one of things we noticed was the latest review of the plugin on the Plugin Directory:

Since the switch from BestWebSoft to Simplywordpress, the quality of this plugin has gone downhill, with numerous problems or issues introduced with what seems like every update. I no longer recommend using this plugin.

For example:

  • Formidable Forms discontinued its Math Captcha integration plugin with this plugin because of breaking updates (like reversing “cptch” to “hctpc” in the code for no discernible reason in an update)
  • As of update 4.4.4 adds ~500 queries associated with visitor tracking or some other nonsense, with many repeated queries, adding over 100ms to every page generation time (not even Memcached could help)
  • Increasingly poor English wording and grammar, leading to possible confusion

The second issue mentioned concerned us and as we will get to in a bit lead to us finding that plugin has multiple security issues caused by that. The third issue also seemed concerning based on us doing some looking into the developer at that point.

The profile page for the developer on wordpress.org lists them as being located in California. Their website, which was registered the day after the transfer of the plugin appears to have happened, though lists an address in the United Kingdom. In one of the prominent instances of a malicious takeover of a plugin there was similar situation where the developers were listing different locations as their supposed location in various places, so that raises a red flag. The server the website is hosted in located in Canada, for what that is worth.

Based on one of the comments from first time the plugin was making admin areas inaccessible the person responding there would seem to not be the person doing the development:

Hey guys thanks as you can see were aware of the plugin issues problem please remove it and accept my sincerest apologies.

I will be creating a mailbox where you can tell the Dev he is usless personally or maybe a skype group

On the website of the plugin’s developer they market their skills as being different than the actual quality of the changes being made to the plugin have shown. From the homepage there is this:

 We are professional programmers who simply love WordPress and can’t wait to make a custom designed plugin for you!

And this:

Hire a team not a guy working in his mom’s basement.

Also on the homepage they twice mention a security service included with their plugins:

With every plugin we have included our “simply-secured” service which helps protect your website from threats.

Every plugin comes with our simply-secured service which protects your site from threats.

Though as we will get to in a moment their Captcha plugins actual introduces security vulnerabilities.

On their services page they make several claims that don’t match the real results with the Captcha plugin:

WP plugin testing and validation

At Simply WordPress, we never improvise on a whim. While we develop fully customized WP plugins, we make sure they can pass validation by WordPress. We build up on the core WP files and add the functionalities you need.

 

Our team is on the ball 24/7 and you can be sure that nothing slips under our radar. If there is so much as a glitch, we fix it right away so your website can keep bringing you profit!

Also worth noting is that on their contact page, the captcha is not generated by someone else’s captcha plugin.

As of today the plugin has been removed from the Plugin Directory, though WordPress continues to not to handle that situation properly and inform people why a plugin has been closed. In this case the developer states that:

We just wanted to let you know the plugin wont be available to be downloaded for a few days as WordPress as asked us to change our brand name as it contains the word “wordpress” which goes against there terms. Obviously we were unaware of this issue and will get this fixed and be back shortly.

Failing at Security Basics

Back in October we announced a new tool that does limited automated security testing of WordPress plugins, so the public can get some idea if a plugin might contain security issues that warrant further review. One of the things that tool checks for is if the plugin registers AJAX accessible function to be accessible to those not logged in as well to those logged in. While there are perfectly safe situations where that happens, what we have found with many vulnerabilities we and others have discovered, is that often time’s plugins are making functionality accessible to those not logged in that they don’t need access to. A month ago we noted how that situation lead to attempts to utilize a vulnerability that had been in the plugin Formidable Forms to exploit a vulnerability in another plugin. That also turns out to be an issue with Captcha starting with version 4.3.6.

As part of the “visitor tracking” mentioned in the review previously mentioned, the file /live-trafic-lib/cptch_traffic_functions.php was added to the plugin and that makes several functions available to anyone whether they are logged in or not.

One of those is the function cptch_get_traffic_record_callback():

911
912
913
add_action( 'wp_ajax_cptch_get_traffic_record',        'cptch_get_traffic_record_callback' );
 
add_action( 'wp_ajax_nopriv_cptch_get_traffic_record', 'cptch_get_traffic_record_callback' );

When that function is requested it doesn’t do any checks on who is making the request before displaying the live traffic. So anyone can monitor visits to the websites, despite it looking like only Administrators are intended to be able to do that.

Other functions that are available allow anyone to block or unblock IP address or whole countries from logging in to the website or utilizing anything that requires a captcha.

What are missing here are not advanced security measures, just the basics, so it looks like the developer doesn’t have a basic understanding of how the security of WordPress plugins should be handled.

Some of the code in those functions isn’t properly handling things to protect against SQL injection, though in our quick check we didn’t see a way it could be exloited.

We have notified the developer of this issues. Due to our overall concern with the plugin and the fact that is currently removed from the Plugin Directory, we decided not to hold back disclosure as we would normally do.

Phoning Home

After you upgrade the plugin to the most recent version an “urgent” message is shown:

At the same If you visit the plugin’s admin page you will receive the following message:

If you click the “Recommend Settings” button shown in the first image or the “Select Prefered Settings” button shown in the second, the plugin will start contacting the developer’s website for a list of blacklisted IP addresses and pass along the site’s address. That would seem to be in violation of the guidelines for plugin’s in the Plugin Directory since there doesn’t seem proper notification of that:

In the interest of protecting user privacy, plugins may not contact external servers without the explicit consent of the user via requiring registration with a service or a checkbox within the settings. This method is called ‘opt in.’ Documentation on how any user data is collected, and used, should be included in the plugin’s readme, preferably with a clearly stated privacy policy.

This restriction includes the following:

  • No unauthorized collection of user data. Users may be asked to submit information but it cannot be automatically recorded without explicit confirmation from the user.
  • Intentionally misleading users into submitting information as a requirement for use of the plugin itself is prohibited.
  • Images and scripts should be loaded locally as part of the plugin whenever possible. If external data (such as blocklists) is required, their inclusion must be made clear to the user.
  • Any third party advertisement mechanisms used within the plugin must have all tracking features disabled by default. Advertisement mechanisms which do not have the capability of disabling user tracking features are prohibited.

The sole exception to this policy is Software as a Service, such as Twitter, an Amazon CDN plugin, or Akismet. By installing, activating, registering, and configuring plugins that utilize those services, consent is granted for those systems.

Moving Off This Plugin

At this point there is a new developer of this plugin that at best isn’t doing enough testing before releasing updates and is introducing other issues to the plugin, so it seems the best thing to do would be to move off of the plugin.

On our websites we have moved back to the last version by the previous developer until we can find a more permanent replacement.

Proof of Concept

The following proof of concept will cause the latest traffic to the website to be shown.

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=cptch_get_traffic_record" method="POST">
<input type="hidden" name="page" value="1" />
<input type="submit" value="Submit" />
</form>
</body>

Timeline

  • December 8, 2017 – Developer notified.
04 Dec

New WordPress Plugins Continue to Use Third-Party Library with Vulnerability Disclosed Years Ago

As we continue to work on expanding what security issues our WordPress plugin security checker tool can check for, one of the things that doing that work has lead us to take notice of is the extent that plugins are using third-party libraries that haven’t been supported in a long time. Just like a plugin that hasn’t been supported, if there has been a security vulnerability that has been discovered, it is unlikely to be fixed. That is the case with the third-party library CSSTidy, which was last updated in 2007.

One of the files in that contains a reflected cross-site scripting (XSS) vulnerability that has been publicly disclosed for years, for example, it was disclosed in one WordPress plugin back in July of 2012. Where we ran across recently across it was in a disclosure by Ricardo Sanchez of it in the plugin AMP Toolbox. That plugin has included the file and therefore been vulnerable since the first release of the plugin, which was only in May of last year.

As we were looking around at this before adding a check for usage of the vulnerable file from the library to our tool we found that it was also used in the plugin Super Simple Custom CSS, which has only been around since July of last year.

In Super Simple Custom CSS the relevant files is located at /super-simple-custom-css/css_optimiser.php and the relevant lines for the issue mentioned in the previous discomposure 142 and 143 of that:

name="url" id="url" <?php if(isset($_REQUEST['url']) &&
!empty($_REQUEST['url'])) echo 'value="'.$_REQUEST['url'].'"'; ?>

The PHP code there checks if a GET or POST input “url” exists and isn’t empty, if both of those are true then the value of the input is output without being escaped.

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.

Also, worth noting with this, is that this is something that the security review that is done of new plugins in the Plugin Directory is supposed to be catching, as one of the items on their security checklist is:

Escape all data before output (See: Data Validation)

Considering that the review team seems to be missing more obvious instances of this type of issue, missing this in this plugin and AMP Toolbox through a third-party library isn’t all that surprising. While we think the reviews would be better if they focused on issues more likely to lead to exploitable vulnerabilities, running newly submitted plugins through our tool would now catch usage of this library. Currently we allow paying customers to use the tool to test plugins that are not currently in the Plugin Directory, but we would be happy to provide free access to that capability to the plugin review team.

Proof of Concept

The following proof of concept will cause an alert box with the message “XSS” to be shown. 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-content/plugins/super-simple-custom-css/css_optimiser.php?url=%22%3E%3Cscript%3Ealert('XSS');%3C/script%3E

Timeline

  • November 27, 2017 – Developer notified.
27 Nov

Did the WordPress Plugin Directory Know That PHP Event Calendar Contains an Exploitable Vulnerability?

A day ago we had what looks to be a request from a hacker for a file that would be located at /wp-content/plugins/php-event-calendar/server/file-uploader/index.php. That would be a file in the plugin PHP Event Calendar. In the Plugin Directory the plugin “has been closed and is no longer available for download”, but no reason is given as to why that is.

In looking around we couldn’t find any public disclosure of a security issue related to that file.

Looking at the code in that file, /server/file-uploader/index.php, in the most recent version of the plugin it simple sets up an instance of the jQuery File Upload Plugin PHP Class library located in the file /server/file-uploader/UploadHandler.php:

13
14
15
16
17
error_reporting(E_ALL | E_STRICT);
require('UploadHandler.php');
 
$custom_dir = $_REQUEST['custom_dir'];
$upload_handler = new UploadHandler(array('upload_dir' => $custom_dir));

In the UploadHandler.php file there is no restriction placed on what type of files can be uploaded through it:

92
'accept_file_types' => '/.+$/i',

So any type of file can be uploaded through it, leading to an arbitrary file upload vulnerability.

Because WordPress continues to refuse to properly handle closed plugins like this, we have no idea if they have known about vulnerability and didn’t warn people about it or if the plugin was closed for some other reason.

Since this vulnerability is being exploited, we are adding the vulnerability to the free data that comes with our service’s companion plugin, so even those not yet using our service can be warned if they are using a vulnerable version of the plugin.

Worth noting here is that this vulnerability was introduced as a security fix, as the relevant changelog entry when it was added was:

Important security fix. Replace Uploadify with jQuery File Upload

As far as we can tell what they replaced was actually more secure and had been inaccurately labeled as leading to an arbitrary file upload vulnerability (as the types of files that could be uploaded were limited).

That change seems like a good example of where our idea of providing an ability for plugin developers to submit information to WordPress on security fixes and then allowing others to review the changes could help to improve security. In the meantime we continue to offer free help to developers dealing with security vulnerabilities (that is an offer that hasn’t been taken up outside of developers we have contacted to let them know of vulnerabilities we or others have discovered).

Proof of Concept

The following proof of concept will upload the selected file to the directory /wp-content/plugins/php-event-calendar/server/file-uploader/.

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

<html>
<body>
<form action="http://[path to WordPress]/wp-content/plugins/php-event-calendar/server/file-uploader/" method="POST" enctype="multipart/form-data">
<input type="file" name="files" />
<input type="submit" value="Submit" />
</form>
</body>
27 Nov

A WordPress Plugin Having Ten Thousand Installs Doesn’t Mean it Will Have Been Reviewed for Security

When it comes to the security of WordPress plugins there are a lot of misconceptions out there (many times they are being repeated by security companies). One of them is that a more popular plugin is going to be more secure because it has been reviewed for security. Here is an example of this claim from a recent thread on Reddit:

it is important to note that there are many thousands of WordPress plugins available, and many of them have only been installed on a handful of websites. Lesser known or less popular plugins will often not have been reviewed for security, and may contain flaws.

For that reason, I recommend avoiding plugins or themes that don’t have several thousand installs (10k+ is a good rule of thumb) on security-conscious installs, unless you can have a developer inspect and vet the code for you.

As is true of so much security advice, there isn’t any evidence presented to back that claim up, which should be a red flag. In our compiling data on WordPress vulnerabilities for our service we haven’t seen evidence that there are many security reviews being done of WordPress plugins, so assuming that more popular plugins have had a security review is not a good idea.

Another way to look at is to see if there are easy to spot vulnerabilities in say a plugin 10,000+ active installations, which happen to have an example of from some checking we were doing related to our tool for doing limited automated security checks of WordPress plugins. One of the checks included in the tool is when user input is being directly output, which could lead to reflected cross-site scripting (XSS). That is something it would have detected in the plugin WP Customer Area up until we notified the developer of the issue and it was fixed.

Before we get the details its worth mentioning that this plugin has a couple of other attributes that get cited as being ones that should be used to decide if a plugin is secure, those being that it was recently updated (as of when we went to notify the developer) and with having positive reviews:

In three files the plugin contained the following line:

 

<input type="hidden" name="page" value="<?php echo $_REQUEST['page'] ?>"/>

That would echo the GET or POST input “page” without escaping it, so for example malicious JavaScript code could be output. Major web browsers other than Firefox provide XSS filtering, so to be exploitable the hacker would have to figure out a way around that or hope that the person to be exploited is using Firefox.

Now there is little bit more to this vulnerability, which gets to why simply using an automated tool, like ours, isn’t enough to determine if there are vulnerabilities. In this case to get that code to run the value of the GET input “page” has to be set to a specific value, in the case of one of the files, /src/php/core-addons/admin-area/templates/private-post-list-page.template.php, it would have to be set to “wpca-list,content,cuar_private_fil”. So that couldn’t also be used for malicious code, but you can set the POST input to another value and it looks like normally the $_REQUEST variable would contain the value of the POST input instead of the GET input.

After we notified the developer of the issue, they released version 7.4.3, which fixes the vulnerability escaping the user input:

<input type="hidden" name="page" value="<?php echo esc_attr($_REQUEST['page']) ?>"/>

There Isn’t an Shortcut to Determining if a Plugin is Secure

With all the claims we have seen so far relate to determining if a plugin is secure, none of them have pointed to a way that you can truly determine if a plugin is secure without actually having a security review done. For example, even using a much more popular than one with 10,000+ active installations, isn’t going to mean that it is secure, as can be seen with another plugin with 300,000+ active installations we looked at recently that had five different vulnerabilities (and still has them, as they still haven’t been fixed).

Our aforementioned tool is able to detect some possible issues and we are continuing to expand what it can do, but it won’t catch a lot of issues, including any of those in 300,000+ active installation plugin at the moment.

If you want to get plugins you use checked over, that is something we offer. If you are paying customer of our service one of the things you get is the ability to suggest/vote for plugins from the Plugin Directory to receive a review from us and you can also order a security review separately from us.

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.

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/admin.php?page=wpca-list%2Ccontent%2Ccuar_private_file" method="POST">
<input type="hidden" name="page" value='"><script>alert(document.cookie);</script>' />
<input type="submit" value="Submit" />
</form>
</body>

Timeline

  • November 21, 2017 –  Developer notified.
  • November 21, 2017 –  Developer responds.
  • November 24, 2017 –  Version 7.4.3 released, which fixes vulnerability.
27 Nov

Easy to Spot Vulnerabilities in WordPress Plugins Can Be an Indication of Poor Development Practices and Further Issues

In testing out a new check we were adding to our tool for doing limited automated security checks of WordPress plugins we ran the plugin ProfileGrid through the tool, since it had previously had the security issue being checked for. That security issue involved usage of a third-party library that hadn’t been updated in 8 years (the library was added to the plugin 9 months ago) and would leak potentially sensitive information about financial transactions. When we ran the plugin through the tool we found that the tool identified that plugin possibly contained a fairly obvious reflected cross-site scripting (XSS) vulnerability. In looking over things we found that there were multiple instances of this issue in the plugin and that it looks like debugging code has been left in the plugin, so the plugin didn’t look exactly production ready in addition to be being insecure.

That wasn’t really surprising when we noticed that one of the developers is CMSHelpLive, which is a company we noted over a year ago at our main blog was still running a version of Joomla that had been EOL’d four and half years ago, while offering to clean up hacked Joomla websites. Over a year later they are still running that version. (It would be hard to make up claims about the poor handling of security by companies involved in security that could outdo what they really do.)

There are several places the reflected XSS vulnerability existed, but as an example let’s take a look one of them that has existed since the first version of the plugin. That involves the usage the outputting of the GET input “search” in the file /admin/partials/user-manager.php, which is used to generate the page /wp-admin/admin.php?page=pm_user_manager. On lines 112 and 115 of that file that input is echo’d without being escaped:

 <input type="text" class="sb-search" name="search" id="search" value="<?php if(isset($_GET['search'])) echo $_GET['search'];?>">
 </div>
 <?php if(isset($_GET['search']) && $_GET['search']!=''):?>
 <div class="sb-search-keyword" id="search_keyword"><?php echo $_GET['search'];?> <span onclick="show_hide_search_text()">x</span></div>

That is a pretty clear cut issue and when we went check things out to make sure there wasn’t any code elsewhere that would restrict that from being exploitable, there wasn’t.

Also, worth noting with this, is that this is something that the security review that is done of new plugins in the Plugin Directory is supposed to be catching, as one of the items on their security checklist is:

Escape all data before output (See: Data Validation)

The plugin is only eleven months old, so the failure to spot this doesn’t seem like it can be blamed on a previous lower standard. While we think the reviews would be better if they focused on issues more likely to lead to an exploitable vulnerabilities, if the team behind this was interested with improving what they are doing now we could certainly help them considering that our automated tool was able to pick this issue up.

In terms of the debugging code, an example were the following commented out lines in the file /admin/partials/add-section.php:

 //echo '<pre>';print_r($_POST);echo '</pre>';die;
 //print_r($identifier);die;

Beyond not needing to be there, in another location what looks to be debugging code was left running in the production version of the plugin. The last line shown below would output any POST input with a request after the code generates a success message (in the file /public/partials/profile-magic-payment-process.php):

 case "success": // success case to show the user payment got success
 echo '<div id="crf-form">';
 echo "<div class='info-text'>".__('Payment Transaction Done Successfully','profile-magic')."</br>";
 echo '</div></div>';
 print_r($_POST);

After we notified the developer of the issue, they released version 2.6.7, which resolved the instances of the plugin directly outputting unescaped output. The example shown above was changed to this:

 <input type="text" class="sb-search" name="search" id="search" value="<?php if(isset($_GET['search'])) echo esc_attr( filter_input(INPUT_GET, 'search', FILTER_SANITIZE_STRING) );?>">
 </div>
 <?php if(isset($_GET['search']) && $_GET['search']!=''):?>
 <div class="sb-search-keyword" id="search_keyword"><?php echo esc_html( filter_input(INPUT_GET, 'search', FILTER_SANITIZE_STRING) );?> <span onclick="show_hide_search_text()">x</span></div>

It isn’t clear why they used an escaping function and filter_input on those, since the escaping function should be enough.

They also removed the relevant debugging code made some other security changes in that version.

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=pm_user_manager&search=%22%3E%3Cscript%3Ealert%28document.cookie%29%3B%3C%2Fscript%3E

Timeline

  • November 20, 2017 – Developer notified.
  • November 21, 2017 – Developer responds.
  • November 23, 2017 – Version 2.6.7 released, which fixes vulnerability.
22 Nov

Our WordPress Plugin Security Checker Identified a Fairly Serious Vulnerability in a Plugin by MailChimp

Recently we introduced a tool to do limited automated security checks of WordPress plugins in the Plugin Directory (and more recently expanded it to check plugins not in the directory). As part of improving that we have been logging any issues identified by the tool in plugins in the Plugin Directory (we don’t log the results for other plugins) and checking some of those to see how well the tool is in identifying real issues.

In one instance, which we will be describing in more detail once the developer has had a chance to fix the vulnerability, we found that a possible issue identified by the tool turned out to not be an issue, but it did indicate a general poor handling of security within the plugin and we then found the plugin has a fairly serious vulnerability. In another instance the tool identified a pretty serious issue in a plugin.

One of the items we check for with the tool is certain instances of local file inclusion (LFI). The checks for that were originally based on a LFI vulnerability we recently found in a brand new plugin as we were in the process of documenting another vulnerability we had found in it through our proactive monitoring of changes made to plugins to try to catch serious vulnerabilities. The checks were then improved by reviewing some other vulnerabilities of that type we had previously found.

Since the LFI checks are something that we haven’t been using elsewhere for some time (as is the case with some of the other checks in the tool are), we didn’t have much idea as to what they might end up picking up in other plugins. So we didn’t know what to expect when we saw that a possible LFI issue had been picked up with the plugin MailChimp for WooCommerce. It turned out that it was a clear cut vulnerability in a plugin with 30,000+ active installations and developed by a company with hundreds of millions of dollars of revenue that makes impressive sounding claims about their handling of security, which is reminder that the security of WordPress plugins is not in great shape.

The file with the vulnerability was /admin/partials/tabs/notices.php, which looked like this before we notified the developer of the issue:

<?php if(isset($_GET['error_notice']) && file_exists(__DIR__.'/errors/'.$_GET['error_notice'].'.php')): ?>
    <?php include(__DIR__.'/errors/'.$_GET['error_notice'].'.php'); ?>
<?php endif; ?>

<?php if(isset($_GET['success_notice']) && file_exists(__DIR__.'/success/'.$_GET['success_notice'].'.php')): ?>
    <?php include(__DIR__.'/success/'.$_GET['success_notice'].'.php'); ?>
<?php endif; ?>

The first line of that file checks if a GET input “error_notice” exists and that file, whose name is partially defined by that input, exists. If the input exists and the file exists then the file is included. Due to a lack of protection against directory traversal, files outside of the intended directory can be included.

That could have allowed a hacker to get code in another file to run that they otherwise would not have been able to. One possible scenario for that sort of issue is if the hacker could upload an image file, they could include PHP code in an image and then that type of vulnerability to cause the PHP code to execute.

The same code is then repeated with a GET input “success_notice”, so an attacker could load two files with one request.

Seeing as this file isn’t intended to be loaded directly, it is an example of why it makes sense to restrict direct access to files that don’t need it, which is something that we check for during the security reviews of plugins we do.

That file has existed in that form since the first version of the plugin, which was released 13 months ago.

A week after we notified MailChimp they released version 2.1.2, which fixes the vulnerability. You wouldn’t know that a vulnerability had been fixed from the changelog, as these are all the entries in the changelog for that version:

  • Fix store deletion on plugin deactivation
  • Correct shipping name is now used on order notifications.
  • Admin orders are now handled appropriately.
  • Skip incomplete or cancelled orders from being submitted when new.
  • fix hidden or inactive products from being recommended.

Not disclosing that a security vulnerability has been fixed could be seen as another questionable element of MailChimp’s handling of security.

The fix made was to remove the file inclusion from the file and instead handle what was done with that code and additional files entirely through one file. That code begins:

<?php if(isset($_GET['error_notice'])): ?>
<div class="error notice is-dismissable">
 <?php
 switch($_GET['error_notice']) {
 case 'missing_api_key':
 _e('MailChimp says: You must enter in a valid API key.', 'mailchimp-woocommerce');
 break;

Getting a Security Review

As this result shows, our plugin security checker is already able to help identify fairly serious vulnerabilities in plugins (and we hope to continue make further improvements going forward to identify even more vulnerabilities). The results of it just point to possible security issues though, so the results need to be reviewed by someone with the proper expertise.

That is where our security reviews come in. If you are a paying customer of service, once you see that a plugin from the Plugin Directory you use contains a possible issue you can suggest it to receive a review as part of the reviews we do through that service (or if someone else has already suggested it, vote for it to receive a review to move it up in the line to receive a review). If you are not interested in the service for some reason, need a plugin not in the Plugin Directory reviewed, or need a review done immediately we also now offer to do the same type of review separately from our main service.

Companies might also want to get a security review done of their plugins to insure their developers are actually producing plugins in line with the company’s claims about their handling of security and to insure that they don’t become aware of an issue when it is already being exploited.

There are other companies that also offer to do reviews of WordPress plugins, but from what we have seen of them, not all of them seem to be qualified to do that (there is a lot of the security industry that doesn’t seem to be qualified to do anything security related). With us you can get some idea of the quality of the reviews based on reviewing the results of previous reviews we have done and the numerous vulnerabilities we have discovered.

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.

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

http://[path to WordPress]/wp-content/plugins/mailchimp-for-woocommerce/admin/partials/tabs/notices.php?error_notice=../../../../../../../test

Timeline

  • November 14, 2017 – Developer notified.
  • November 14, 2017 – Developer responds.
  • November 21, 2017 – Version 2.1.2 released, which fixes vulnerability.
22 Nov

Authenticated Local File Inclusion (LFI) Vulnerability in Vmax Project Manager

We recently noticed an authenticated arbitrary file upload vulnerability in the plugin Vmax Project Manager. While writing up the details of that we were tracing back the code that would be involved in that and at first we couldn’t figure out how part of it would work. Then we figured that out and noticed that there is also an authenticated local file inclusion (LFI) vulnerability in the plugin.

The plugin makes its main admin page available to anyone with the “read” capability, which is a capability that provides access to Admin dashboard and is a capability provided to Subscriber-level users and above (in the file /vpm.php):

37
$menu = add_menu_page('Vpm Project Manager', 'Project Manager', 'read', 'vpm-project', 'vpm_project' );

The function called includes the file /view/index.php:

84
85
86
87
function vpm_project() {
	$body	=	'project';
	include( VPMDIR . '/view/index.php');
}

In that file, if the GET input “module” exists, its value is used in an include statement:

5
6
7
8
9
10
if(isset($_GET['module'])) {
	$body = sanitize_text_field($_GET['module']);
}
 
// Loads the body page for each specific menu/tasks inside the plugin.
include $body.".php";

There is no restriction on directory traversal being used there, so files outside of the directory that files are intended to be included from can be accessed.

We used this vulnerability to as the basis for adding checking for some local file inclusion (LFI) vulnerabilities to our security checker for WordPress plugins.

We notified the developer of the issue on October 23. They responded the same day that it would be fixed within two weeks, but so far no new version of the plugin has been released. 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:///wp-admin/admin.php?page=vpm-project&module=..%2Ftest

Timeline

  • October 23, 2017 – Developer notified.
  • October 23, 2017 – Developer responds.
22 Nov

Authenticated Arbitrary File Upload Vulnerability in Vmax Project Manager

A month ago we wrote about how the security review of newly submitted plugins to the WordPress Plugin Directory needs improvement. One of the newly introduced plugins that lead to that was the plugin Vmax Project Manager. We came across the plugin through our proactive monitoring of changes made to plugins to try to catch serious vulnerabilities, due to the possibility of an arbitrary file upload vulnerability in the plugin.

What got flagged during our monitoring was the following line in the file /model/function.php:
131
move_uploaded_file($_FILES['file_name']['tmp_name'], $upload_dir['basedir'] . '/vpm/project_file/' . $_FILES["file_name"]["name"]);

That alone doesn’t tell you much, as all you can tell is it looks like an uploaded file is being saved to the filesystem with its filename intact.

With the fuller context of the function it is in you can see that there are no checks being done there that might restrict what types of files might be uploaded:

126
127
128
129
130
131
function vpm_file_create() {
	$data['vpm_project_id']	=	sanitize_text_field($_POST['vpm_project_id']);
	$data['file_name']		=	$_FILES['file_name']['name'];
	$data['note']			=	sanitize_text_field($_POST['note']);
	$upload_dir = wp_upload_dir();
    move_uploaded_file($_FILES['file_name']['tmp_name'], $upload_dir['basedir'] . '/vpm/project_file/' . $_FILES["file_name"]["name"]);

There still could be code elsewhere that restricts what type of files can be uploaded, though more often than not the code doing any checks would be in that.

That still doesn’t mean there is a vulnerability as the code still needs to be accessible in a way that it can be abused.

That function is called in one place, in the file /controller/controller.php:

66
67
	else if($task	==	'create_file') {
		vpm_file_create();

That value of $task comes from the beginning of the file, which also checks for a valid nonce:

3
4
5
6
7
8
9
10
$nonce 	=	sanitize_text_field($_POST['nonce']);
$task 	=	sanitize_text_field($_POST['task']);
 
$nonce_verify	=	wp_verify_nonce($nonce , 'vpm-project-manager');
 
 
// Only if nonce values submitted with post calls are verified, those db query functions will be executed
if ($nonce_verify == true) {

At that point we went to look at the issue from inside WordPress, though we later went to back to tracing the code used and spotted another vulnerability in the plugin, which will be the subject of another post.

On the plugins’ page to manage a project there is a section for files:

That page is available to anyone with the “read” capability, which is a capability that provides access to Admin dashboard and is a capability provided to Subscriber-level users and above (in the file /vpm.php):

37
$menu = add_menu_page('Vpm Project Manager', 'Project Manager', 'read', 'vpm-project', 'vpm_project' );

When you click Create you are taken to a page to upload a file:

If you try to upload a file with a .php extension that is not stopped:

So anyone that is logged in to WordPress with the “read” capability can upload arbitrary files.

We notified the developer of the issue on October 18. They responded five days later and said that it would be fixed within two weeks, but so far no new version of the plugin has been released. 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.

Timeline

  • October 18, 2017 – Developer notified.
  • October 23, 2017 – Developer responds.
22 Nov

Arbitrary File Upload Vulnerability in Wallable

A month ago we wrote about how the security review of newly submitted plugins to the WordPress Plugin Directory needs improvement. One of the newly introduced plugins that lead to that post was the plugin Wallable. We came across the plugin through our proactive monitoring of changes made to plugins to try to catch serious vulnerabilities. The possible vulnerability that had been identified in the plugin was an arbitrary file upload vulnerability and when we went to look into that we found that not only did that issue exist, but the plugin was fairly insecure in a more general fashion.

In three locations in the code the plugin would upload arbitrary files. Two of those are located in the function frontend_do_tasks().  When we went to test out exploiting one of those we found that the plugin would cause a fatal error before that could happen when not logged in to WordPress.

The final location is in the function backend_do_tasks(), which based on that name, would seem to be off limits to those not logged in, but that turned out to not be the case.

Near the beginning of the plugin’s main file it checks if is_admin() is true:

21
if (is_admin()){

That function will tell you if “the Dashboard or the administration panel is attempting to be displayed” and can be true when not logged in to WordPress, depending on what is trying to be accessed.

If that is true, the function backend_do_tasks() will be run:

40
41
42
43
44
if (@$_REQUEST['mod'] == 'rawmode'){
	add_action('wp_loaded', array(&amp;$wallable_controller, 'backend_do_tasks'));
}else{
	$wallable_controller-&gt;backend_do_tasks();
}

When the function backend_do_tasks() runs it doesn’t do any check as to who is trying to access it (in the file /classes/wallable_controller.php):

294
295
296
297
298
299
function backend_do_tasks() 
{
	//DO tasks
	$task = @$_REQUEST['wallable_task'];
	switch ($task)
	{

When the value of the GET or POST input “wallable_task” is set to “save_items” the following code will run:

303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
case 'save_item':
	if ((int)$_REQUEST['item_id'] &gt; 0){
		$file_name_id = $_REQUEST['item_id'];
	}else{
		$query = "SELECT max(id) as max_id FROM ".$this-&gt;_db-&gt;prefix."wallable_dashboard_items";
		$rows  = $this-&gt;_db-&gt;get_results($query, OBJECT);
		$file_name_id = (int) $rows[0]-&gt;max_id;
		$file_name_id++;
	}
	if (!empty($_FILES['image']['name'])){					
		$file_info = explode('.', $_FILES['image']['name']);
		$file_type = $file_info[count($file_info)-1];
		$file_name = $file_name_id.'.'.$file_type;
		if (!is_dir(wallable_upload.'/dashboard')){
			wp_mkdir_p(wallable_upload.'/dashboard');
		}
		$dest_file = wallable_upload.'/dashboard/'.$file_name;
		if (move_uploaded_file($_FILES['image']['tmp_name'], $dest_file)){

That will save a file to the directory /wp-content/uploads/wallable/dashboard/ with a name based on the GET or POST input “item_id”, if specified. It doesn’t restrict what types of files can be uploaded.

We notified the developer of the issue on October 23. They responded that it would be fixed with the next upgrade. Subsequent changes have been made to the plugin, but the issue has not been fixed. 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 upload the selected file to the /wp-content/uploads/wallable/dashboard/ with the file name that starts”1000.” and ends with the uploaded file’s file extension.

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="wallable_task" value="save_item" />
<input type="hidden" name="item_id" value="1000" />
<input type="file" name="image" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • October 23, 2017 – Developer notified.
  • October 23, 2017 – Developer responds.
21 Nov

Cross-Site Request Forgery (CSRF)/Cross-Site Scripting (XSS) Vulnerability in Simple Events Calendar

While looking in to what turned out be a false report of a vulnerability in the plugin Simple Events Calendar, we noticed there is a cross-site request forgery (CSRF)/cross-site scripting (XSS) vulnerability in the plugin.

When the plugin’s admin page is requested, the function that generates that page checks if a new event has been submitted with the request using the following code (in the file /simple-events-calendar.php):

155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
if(isset($_POST['store_event'])) {
	if($_POST['event']['event_title']) { // New entries at least have to have a title, if none then nothing is written to DB
		global $wpdb;						
		$table_name = $wpdb-&gt;prefix . "simple_events";
		$event = $_POST['event'];
		$event_start = mktime($event['event_start_hr'],$event['event_start_mn'],0,$event['event_start_month'],$event['event_start_day'],$event['event_start_yr']);
		$event_end = mktime($event['event_end_hr'],$event['event_end_mn'],0,$event['event_end_month'],$event['event_end_day'],$event['event_end_yr']);
		$newEvent['event_start'] = $event_start;
		$newEvent['event_end'] = $event_end;
		$newEvent['event_title'] = $event['event_title'];
		$newEvent['event_desc'] = $event['event_desc'];
		$newEvent['event_url'] = str_replace(" ", "", $event['event_url']);
		$newEvent['event_loc'] = $event['event_loc'];
		$newEvent['event_loc_url'] = str_replace(" ", "", $event['event_loc_url']);
		$newEvent['event_label'] = strtolower(str_replace(" ", "", $event['event_label']));
 
		$wpdb-&gt;insert( $table_name, $newEvent );

That code doesn’t check for a valid nonce, so it is susceptible to cross-site request forgery (CSRF). That code saves user input, from the POST input “event”, without doing any sanitization.

The same is true for similar code right below that code for updating an event.

Then for example, on line 693 of the file the value of one of the items taken from user input, “event_title”, is output without being escaped:

<th class="eventtitle" colspan="3"><form method="post"><input type="hidden" name="event_id" value="<?php echo $details['id'];?>" /><input type="submit" name="edit" value="<?php _e('Edit',SE_TEXTDOMAIN);?>" class="iconsprite editicon" /><input type="submit" name="delete" value="<?php _e('Remove',SE_TEXTDOMAIN);?>" class="iconsprite binicon" /></form> <?php echo stripslashes($details['event_title']);?></th>

The lack of sanitization and escaping leads to the possibility of cross-site scripting (XSS).

We notified the developer of issue on November 10. We have yet to hear back from them and the vulnerability has not been fixed so far. The plugin was last updated nearly a year ago, so it may no longer be supported. 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 an alert box with any accessible cookies to be shown on the page /wp-admin/admin.php?page=simple-events, when submitted while logged in to WordPress.

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

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/admin.php?page=simple-events" method="POST">
<input type="hidden" name="store_event" value="test" />
<input type="hidden" name="event[event_title]" value='"><script>alert(document.cookie);</script>' />
<input type="hidden" name="event[event_loc]" value="" />
<input type="submit" value="Submit" />
</form>
</body>

Timeline

  • November 10, 2017 – Developer notified.