WordPress released version 5.5.2 yesterday, which fixed a reflected XSS vulnerability we reported earlier this year. The root cause of this issue is a bug in the way WordPress determines a user’s current page, and which may cause a few other problems as well.

Are You Affected?

This vulnerability is exploitable on every WordPress site and user account. For the exploit to be successful, the attacker must trick an unsuspecting user into clicking on a malicious link or visiting a booby-trapped website.

A successful attack results in the execution of rogue JavaScript code, which can be designed to perform privileged actions on the victim’s behalf — for example, creating new administrative accounts or storing backdoors on the site to maintain unauthorized access.

Technical Details

The root cause of this issue is a bug in the way the $pagenow global variable has been set-up.

reflected_xss_wordpress_bug

Put shortly, these regexes are trying to determine which file is being loaded. They also mitigate simple spoofing techniques like trying to set $pagenow’s value to “test.php” by visiting /wp-admin/index.php/wp-admin/test.php.

That being said, those regexes also contain a very subtle mistake which has gone unnoticed for quite some time: they are using the special dot character.

In a vanilla regular expression, the dot character matches against basically every character except newlines. Add that to the fact that PHP_SELF can contain decoded user-input coming straight from the URI, and you may see how it could cause some issues.

Getting back to our previous example with what we now know, if you want $pagenow to contain test.php, you can force it by visiting /wp-admin/index.php/%0a/wp-admin/test.php.

With that in mind, let’s see how deep the rabbit hole goes.

reflected_xss_wordpress_hook_suffix

$pagenow may be used to initiate the $hook_suffix global variable, which is also used quite a bit in WordPress.

reflected_xss_wordpress_get_method

This is the get method of the WP_Screen class, used to obtain information about the screen a user is currently viewing. It essentially generates an object of that class and sets the properties according to the information it has and some of it, as you may have guessed, is coming from $hook_suffix. From the above screenshot, you can see that both the id and base properties are now tainted with user-input as a result of this functionality.

Since the resulting instance is used a lot by plugins who often rely on the get_current_screen function to get information about the page being visited, this is a big deal. It also means the $current_screen global variable can contain user-input too!

reflected_xss_wordpress_get_current_screen

This improper set-up of the global $pagenow variable leads to the XSS vulnerability we found. As you can see, $current_screen->id is echoed in a JavaScript context, we’ll leave it up to our readers to guess how this could have been abused.

Breaking Assumptions

While an XSS vulnerability is kind of bad, we believe the most interesting piece to this attack vector is the myriad of assumptions it is challenging.

We believe it will lead to very interesting issues in the future — and expect that we will most likely encounter this technique again.

Conclusion

To protect against this XSS vulnerability, we strongly encourage WordPress users to update their site to the latest version available as soon as possible — version 5.5.2 at the time of writing.

Users who are unable to update immediately can leverage the Sucuri Web Application Firewall or equivalent technology to virtually patch the vulnerability.

Leave a Reply

Your email address will not be published.