19 Dec

The Results of Our WordPress Plugin Security Checker Lead to More Serious Issues in Plugin

We recently introduced a new tool to check WordPress plugins in the Plugin Directory for possible security issues. As we continue to look to how we can improve that, we are recording any issues identified by it, so that we can see what kinds of things it is identifying and where they might be room to refine the checks.

In looking over one of the plugins that it identified issues in, what we found was that one of the possible issues was not likely to be exploitable, but did point to the possibility that the pluginswas not all that securely written in general and led to us finding a more serious vulnerability in the plugin. That is obviously one data point, but it does indicate that it might be useful for plugins that are identified as having possible issue to have proper security review done. For those using our service they can then vote/suggest to have the plugin receive a review from us and for those that some reason are not interest in the service can always order a review separately.

Unneeded AJAX Registration

The checker identified two possible issues in the plugin Rich Reviews. The first issue identified was:

  • The plugin makes functions accessible to users that are not logged in to WordPress through its AJAX functionality. They should be checked to make sure they are intended to be accessed by those not logged in.

That check will identify code that is safe and intentionally set up to allow those not logged in to access, as well instances where it isn’t intended. In this case it identified something that doesn’t look like it was intended to be accessed by those not logged, but what can be done by those not logged is basically harmless.

For two similar functions the plugin makes them accessible to both those logged in to WordPress and those not logged in. Here is how that looks for one of the functions (the nopriv line allows those not logged in to access it):

add_action( 'wp_ajax_rr_dismissed_help_notice', array(&$this,'ajax_rr_dismissed_help_notice' ));
add_action( 'wp_ajax_nopriv_rr_dismissed_help_notice', array(&$this,'ajax_rr_dismissed_help_notice' ));

Here is the related function:

function ajax_rr_dismissed_help_notice() {
	$this->parent->shopApp->options->update_option('dismissed_help_notice', TRUE);

That will dismiss a help notice from the plugin, which at best might be a bit of a nuisance if someone not logged in caused that to happen. At the same time since that is something that is only shown in the admin area of the plugin, there isn’t any reason that someone not logged in showed be accessing it.

Unintended Validation?

The second issue identified was:

  • User input is being directly output, which could lead to reflected cross-site scripting (XSS).

That would have been caused by any the following three lines in the file /lib/rrShopApp/views/edit_single_index.php:

<form name="single-product-index" id="edit-index-<?php echo $_GET['id']; ?>" method="post">
<input type="text" name="id" value='<?php echo $_GET['id']; ?>' disabled/>
<form method="post" name="delete-listing-<?php echo $_GET['id'];?>" id="deleteListingForm">

In each of those the value of the GET input “id” is being output without being escaped, which could lead to reflected cross-site scripting (XSS). We say lead to because it could be that input was sanitized prior in the code, which would preclude the need for escaping, or in this case was validated, which would limit what could be output.

Before any of those lines can be reached the following check is done:

if (isset($options['product_catalog_ids'][$_GET['id']])) {

So unless the value of the “id” input is contained in $options[‘product_catalog_ids’] the code doesn’t run.

While it is unlikely that could be exploited, it still would be a good idea to escape those previous lines even with that in place and in this case it seems possible that check that limits was not intentionally done with security in mind, so the lack of escaping could be an indication of poor security elsewhere.

Persistent Cross-Site Scripting (XSS)

We recently have been testing out a security tool named OWASP ZAP to see if an automated tool (or more accurately considering the work that needs to be done to get it to be useful, semi-automated) could provide some additional value beyond the kind of security checks we already do during our security reviews. We ran that over the admin pages of this plugin and it identified a possible issue. The issue is something that our normal checking during a security review would have caught as well, though. When we went to look into the details we found that the issue was more serious than what the tool identified.

What the tool identified was that on the plugin’s Options page there was a reflected cross-site scripting (XSS) vulnerability. When checking other plugins we have found that type of issue has been a false positive because it doesn’t understand the implication of a nonce in the request that was included in the requests. In this case there wasn’t a nonce, which meant that there was a cross-site request forgery (CSRF) issue when saving the plugin’s settings as well. That can be combined with XSS leading to a CSRF/XSS vulnerability. That is probably more serious than a reflected XSS issue, though neither is likely to be exploited.

What an automated tool like that doesn’t do is understand the underlying code that leads to the vulnerability. With this vulnerability, as we went to look at the underlying code we found that it was more serious.

The saving of the plugin’s settings is handled by the function update_options() in the file /lib/rich-reviews-options.php. The only requirement in that code to change the settings is that the POST input “update” is set to “rr-update-options”:

public function update_options($init = null) {
	if($init == true ) {
		foreach($this->defaults as $key => $val) {
			if(!$this->get_option($key)) {
			  $this->update_option($key, $val);
	if (isset($_POST['update']) && $_POST['update'] === 'rr-update-options') {

That function is called when a new instance of the class RROptions is being constructed. A new instance of that class is created when new instance of the class RichReviews is being constructed. That in turns in happens on the last line of the plugin’s main file:

$richReviews = new RichReviews();

That means that any time the plugin is loaded, which would be when any page on the frontend or the backend of WordPress is loaded, as long as the plugin is activated, it would be possible to change the plugin’s settings. So someone could send a request to homepage of the website and cause the plugin’s settings to be changed. Through that an attacker could cause malicious JavaScript code to be output on frontend and or admin pages.

Lesser Issues Fixed

On November 3 we notified the developer of all those issues and they replied later that day. On November 13 the two lesser issues that our checker identified were fixed, but the more serious issue with the changing of the settings was not fixed. The version number was not changed, so if anyone that is already using version 1.7.3 would not be prompted to update. On November 14, we notified them that the more serious issue was unresolved. They responded the next day, that would be worked on. Nothing has happened since then.

Proof of Concept

The following proof of concept will change the value of the plugin’s Submit button text to JavaScript code that would show an alert with any available cookies, which would run both frontend pages that show that as well as on the Options page.

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

<form action="http://[path to WordPress]" method="POST">
<input type="hidden" name="update" value="rr-update-options" />
<input type="hidden" name="form-submit-text" value='"><script>alert(document.cookie);</script>' />
<input type="submit" value="Submit" />


  • November 3, 2017 – Developer notified.
  • November 3, 2017 – Developer responds.
  • November 13, 2017 – Developer release new version of 1.7.3, which resolve less serious issues.
  • November 14, 2017 – Developer notified of unresolved issue.
  • November 20, 2017 – Developer responds.

Concerned About The Security of the Plugins You Use?

When you are a paying customer of our service you can suggest/vote for the plugins you use to receive a security review from us. You can start using the service for free when you sign up now.