1 Dec 2017

Vulnerability Details: Reflected Cross-Site Scripting (XSS) Vulnerability in User Role Editor

From time to time a vulnerability is fixed in a plugin without the discoverer putting out a report on the vulnerability and we will put out a post detailing the vulnerability so that we can provide our customers with more complete information on the vulnerability.

All three changelog entries for version 4.38 of the plugin User Role Editor are security related, possibly indicating that some sort of a security review had been done of the plugin. The first entry is:

Security: XSS vulnerability was fixed at URE’s options page. Bug was discovered and fixed at tab index value numeric type checking. Tab index value is additionally escaped before output also.

Looking at the changes made in that version it wasn’t hard to find the fixes related to this and to see where there seems like a possible security hardening could be implemented to avoid the issue that occurred here from leading to a vulnerability again.

In the file /includes/classes/user-role-editor.php line 782 was changed from this in the previous version:

$ure_tab_idx = $this->lib->get_request_var('ure_tab_idx', 'int');

to this:

$ure_tab_idx = (int) $this->lib->get_request_var('ure_tab_idx', 'post', 'int');

One of the changes is to restrict the value of $ure_tab_idx to an int using (int). The previous code passed “int” to the function get_request_var(), which turns out to be important in understand what went wrong.

The function get_request_var(), which is located in the file /includes/classes/base-lib.php, is as follows:

public function get_request_var($var_name, $request_type = 'request', $var_type = 'string') {
 
	$result = 0;
	if ($request_type == 'get') {
		if (isset($_GET[$var_name])) {
			$result = filter_var($_GET[$var_name], FILTER_SANITIZE_STRING);
		}
	} else if ($request_type == 'post') {
		if (isset($_POST[$var_name])) {
			if ($var_type != 'checkbox') {
				$result = filter_var($_POST[$var_name], FILTER_SANITIZE_STRING);;
			} else {
				$result = 1;
			}
		}
	} else {
		if (isset($_REQUEST[$var_name])) {
			$result = filter_var($_REQUEST[$var_name], FILTER_SANITIZE_STRING);
		}
	}
 
	if ($result) {
		if ($var_type == 'int' && !is_numeric($result)) {
			$result = 0;
		}
		if ($var_type != 'int') {
			$result = esc_attr($result);
		}
	}
 
	return $result;
}

You can see in there that there is code that would limit values that are supposed to be ints from being some other value. That didn’t work in this instance, which upon looking closely at the code we realized was because the old code didn’t pass the “int” value to the $var_type but instead to the $request_type. So instead of being restricted to an int, the value was escaped using esc_attr().

That function could be changed to catch such a situation where the $request_type is set to a value it shouldn’t (it should only be one of three values) to prevent this mistake here from happening. We have made that suggestion to the developer of the plugin.

While there was still escaping done, the value was output into JavaScript code in the file /includes/settings-template.php, so the escaping is not effective at preventing exploitation:

jQuery("#ure_tabs").tabs("option", "active", <?php echo $ure_tab_idx; ?>);

What seems notable about this vulnerability is that in our testing the XSS filtering in several browsers didn’t stop exploitation, as it does for most reflected XSS vulnerabilities.

Proof of Concept

The following proof of concept will cause any available cookies to be shown in alert box, 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=settings-user-role-editor.php&ure_tab_idx=1%29%3Balert%28document.cookie

Leave a Reply

Your email address will not be published.