18 Jul 2023

Authenticated Persistent Cross-Site Scripting (XSS) Vulnerability Fixed in Rank Math SEO

The changelog for the latest version of the WordPress plugin Rank Math SEO, which has 2+ million installs, suggested that a security vulnerability had been fixed, but didn’t credit a discoverer. (It did mention a company that redirects vulnerability reports away from developers and WordPress.) Checking in to that, we found that a minor authenticated persistent cross-site scripting (XSS) vulnerability exploitable through a shortcode had been fixed.

Only one change was made in that version, which makes it easy to see what was going on.

In the file /includes/frontend/class-shortcodes.php, the following line was changed:

97
echo '<div class="' . $this->get_contact_classes( $allowed, $args['class'] ) . '">';

Escaping through the function esc_attr() was added to that:

97
echo '<div class="' . esc_attr( $this->get_contact_classes( $allowed, $args['class'] ) ) . '">';

The reason for that is that the value of user input, in the form shortcode attribute, $args[‘class’], is output through the function get_contact_classes( ), without it being sanitized, validated, or escaped:

83
84
85
86
87
88
$args = shortcode_atts(
	[
		'show'  => 'all',
		'class' => '',
	],
	$args,
97
echo '<div class="' . $this->get_contact_classes( $allowed, $args['class'] ) . '">';
142
143
144
145
146
147
148
149
150
151
private function get_contact_classes( $allowed, $extra_class ) {
	$classes = [ 'rank-math-contact-info', $extra_class ];
	foreach ( $allowed as $elem ) {
		$classes[] = sanitize_html_class( 'show-' . $elem );
	}
	if ( count( $allowed ) === 1 ) {
		$classes[] = sanitize_html_class( 'show-' . $elem . '-only' );
	}
 
	return join( ' ', array_filter( $classes ) );

That would allow malicious JavaScript to be output on pages with the shortcode. The escaping added stops that from happening.

The risk caused by that is limited since an attacker would need to be able to create a WordPress post and not be able to include JavaScript otherwise, which is only true of two of the four WordPress roles that can create posts. Untrusted individuals usually wouldn’t have the ability to create new posts.

We checked for any similar issues in the code from other shortcodes provided by the plugin and didn’t find any additional issues.

Proof of Concept

Creating a new post as a user with the Author role, which doesn’t have the unfiltered_html, with the following shortcode will cause an alert box with any available cookies to be shown when hovering over the plugin’s content on the page.

[rank_math_contact_info class='" onmouseover="alert(document.cookie);"']

Leave a Reply

Your email address will not be published.