29 Jul

False Vulnerability Report: Multiple Stored XSS Vulnerability in Clicky by Yoast 1.4.3

As part of our cataloging the vulnerabilities in WordPress plugins for our service we come across false reports of vulnerabilities from time to time. So that others don’t spend their time looking over these as well, we post our findings on them.

If you are going to promote your “web application security scanner” as being “False positive free” as Netsparker does, it would probably be a good not to release advisories for vulnerabilities that don’t actually exist, using data from that tool. But that is what Netsparker did with several recent advisories for WordPress plugins, including a claim of a multiple persistent cross-site scripting (XSS) vulnerabilities in Clicky by Yoast.

Right off the bat something looked wrong here, the URL in the proof of concept to exploit the claimed vulnerability, /wordpress/wp-admin/options-general.php?page=clicky, is only accessible by Administrator level users. Seeing as they would normally have the unfiltered_html capability, which permits them to use the equivalent of cross-site scripting, this would seem to not be a vulnerability on its own. If there was a cross-site request forgery (CSRF) vulnerability in saving the plugin’s settings, then there could still be an issue by using that to allow the persistent XSS that is supposed to exist in that code to be exploited.

The settings are saved in the function config_page() in the file /click.php, in the claimed vulnerable version, 1.4.3, there is proper protection against CSRF is in place:

function config_page() {
	$options = clicky_get_options();
	if ( isset( $_POST['submit'] ) ) {
		if ( ! current_user_can( 'manage_options' ) ) {
			die( __( 'You cannot edit the Clicky settings.', 'clicky' ) );
		check_admin_referer( 'clicky-config' );

What comes right after that in the code makes it harder to understand where the claim that there was a vulnerability could have come from, as all of the supposedly vulnerable inputs are run through the sanitize_text_field() function when their value is brought in to the plugin:

		foreach ( array( 'site_id', 'site_key', 'admin_site_key', 'outbound_pattern' ) as $option_name ) {
			if ( isset( $_POST[ $option_name ] ) ) {
				$options[ $option_name ] = sanitize_text_field( $_POST[ $option_name ] );
			} else {
				$options[ $option_name ] = '';

Netsparker doesn’t provide any information on why they think that doesn’t properly sanitize them in this case, which would be important to know for other situations where the other restrictions in place here are not in place.