XSS (or cross site scripting) attacks are a common method to maliciously execute actions against a website installation. In particular this type of attack vector is useful when dealing with a CMS like WordPress where you have administrative user accounts to target.
What I’ll go through in this post is exactly how to capitalize on a particular (old) WordPress plugin vulnerability to deliver a persistent XSS injection (not logged into WordPress) that will later be executed by someone logged into WordPress with higher privileges, such as an administrator.
This is debatable, but to simplify things it would be easiest to describe XSS attacks as being two high level methods : persistent (stored) and reflective. For full explanations as well as the subsidiary types of XSS attacks, you might want to read the OWASP Types of Cross Site Scripting information page.
Another method of delivery in the reflective XSS scenario would be to basically make your own URL redirect / shortening system, which bypasses using a service like bit.ly where they already are filtering “suspicious” URLs from being shortened. Register a free no-ip.org or dyndns.com domain, point it to a server and 301 redirect incoming requests for the url to the payload URL. You can use nginx or apache to do this quite easily.
As mentioned, we want to get the NONCE because we need to generate an HTTP post to create the user. The NONCE is a protection mechanism in WordPress, created for the very purpose of preventing what we are about to do.
All we are doing is generating a GET request to /wp-admin/user-new.php , using regex to parse the nonce value and storing it in a variable that we will use later, called “nonce”.
In the above snippet, all you really need to be concerned with is the first line of code that is being assigned to the “params” variable. We are creating a POST request string, using the nonce obtained earlier, and creating a username, password and email with the administrator role being assigned.
If you store this in a wordpress post and execute it, while logged in, you should see the user created when you navigate over to the users section. Try it and see!
Looks minimal, right? Its important to test the code on your own installation before taking these steps because its incredibly difficult to debug after its minified for obvious reasons!
Now that we’ve minified our code, we want to encode it so that it can be delivered in a URL and wont break your browser or generate an error before even executing.
Hard to discern whats going on now in the above snippet, right? Thats partly the point. Also important to sanitize your code and ensure it executes. But before you try the above snippet in a URL, you want to encode the characters to a URL friendly format. This means things like commas and brackets need to be converted to a format that the browser can process.
See the difference? Now if you were going to inject the code in search results (for example, if you can). You will want to append it to the url in a fashion similar to the following example :
If the above is added as a value to a vulnerable GET or POST variable, then the code will either be executed on the page when the URL is loaded (Reflective) or executed when the page with the code is executed (stored).
The vulnerability allowed code to be injected in a logging area within the All in on SEO administration area that tracks bad bots (“track blocked bots setting”). When a logged in administrator views this log page, the XSS code illustrated above executes and an admin user is created!
Makes sense right? Hopefully this overview gives you a better idea of how XSS against WordPress in particular works. There are many ways to prevent these types of attacks. I would resist encouraging people to install WordPress security plugins like WordFence.
Instead I would recommend investigating security conscious web hosting solutions that implement a WAF (web application firewall) in conjunction with the web services. If you manage your own environment, you could investigate the implementation of Naxsi for Nginx or ModSecurity for Apache.
Another solution that would at least prevent new users from being created would be to potentially wrap the entire administration area in HTTP authentication. This is something that an attacker wouldn’t be able to easily bypass because they would need the username and password to submit POST variables via an XSS attack. This can be done with an .htaccess file or nginx configuration.
Of course, no solution will offer 100% protection but a solid WAF will definitely be more effective. That and always keeping your plugins up to date, of course!