Statistics from w3techs suggest that 1 out of 4 websites (around 25%) on the internet are powered by WordPress. WordPress’ popularity is derived from its ease of setup and use, its contributing community, and the big repertoire of plugins and themes that are available.
Why is WordPress Such a Common Target?
Even though WordPress is a beginner friendly web application, like every other platform it has its own issues and limitations. One of the most voiced security issues is that it is possible and very easy to bruteforce login credentials. WordPress’ advice on this is to install a security plugin, protect the WordPress login page with a .htpasswd file (HTTP authentication), and of course use strong credentials.
However many users, especially the unexperienced ones do not take these extra security measures onboard. They use very weak credentials and do not setup any additional layers of security on their websites, thus making WordPress a good target for brute force attacks.
How to Bruteforce WordPress Websites and Blogs Running on an Internal Networks and Behind Firewalls
WordPress blogs aren’t always used for publicly accessible websites. They are also frequently used as websites in intranets for employees. Typically Intranets are not reachable from the outside (the internet) because they are sitting behind a firewall. Though WordPress websites running in intranets are still at risk; attackers can effectively brute force a WordPress blog or website in an internal network via XSHM, without having direct access to it.
What is XSHM?
XSHM is an abbreviation for Cross Site History Manipulation. It is a security breach in the Same Origin Policy, which is used by web browsers to prevent different websites from retrieving information from each other when a user is accessing them both. This means that website A can not read the content of website B when both are accessed at the same time in different browser tabs.
However, there are some side channel attacks that can be used to leak certain information even though the same origin policy is in place. XSHM is one of them and below is an example:
- An attacker creates an iframe on a website he controls (website A) and points it to a page on website B that has a conditional redirect. For example the iframe points to login.php, which when accessed redirects the user to index.php if he is logged in.
- The attacker retrieves the history.length value of the browser tab.
- The attacker updates the iframe to point to index.php.
- When the user accesses the iframe again, the attacker retrieves the new value of the history.length property again and compares it to the one in step 2.
Since the web browser does not increase the history.length value if the URL the iframe is the same as the URL the user is currently browsing, then it is easy to determine if the user is logged into WordPress or not. Therefore if the history.length value remains the same, it means that the user was redirected to index.php, which means he is logged in.
How to Identify WordPress Websites on a Local Network
WordPress has a unique redirect, that makes it really easy for attackers to spot. If a user is not logged in and visits the page /wordpress/wp-admin/, he is redirected to:
Therefore to find WordPress websites on an internal network an attacker can send the victim a link with a XSHM payload, that tries the above redirect on a range of internal IP addresses such as 192.168.1.1/24 when a user clicks the link.
How does bruteforcing WordPress logins work with XSHM?
Now that the attacker identified the WordPress websites he can start the brute force attacks with XSHM, even though he does not have direct access to it. This is possible due to the fact that WordPress does not have a token to prevent logins via CSRF. There is a general misunderstanding of whether or not CSRF Tokens are necessary in login forms.
Note: Tokens in login pages are necessary. It is generally advised to secure your WordPress login page with Tokens to prevent these type of attacks. There are several other attack vectors that use the login CSRF as entry points, which are not obvious but can have serious impacts, such as logging the user in an attacker’s account without his knowledge and steal private information. It might also be possible to abuse an otherwise not reachable Stored Cross-site Scripting (XSS) vulnerability.
WordPress also provides a redirect_to form field in its login, which lets the attacker specify where he wants the victim to be redirected after a successful login. This suits perfectly the attacker’s XSHM attack. He can now use a website which makes a CSRF attack based on GET parameters and supply different username / password combinations. The attack works as follows:
- Retrieve the value of the history.length property of the victim’s browser tab.
- Point the src of the iframe to the page that carries out the CSRF attack. This can be done by using a self-submitting form to the wp-login page with a username / password combination.
- Point the iframe to the path from the redirect_to parameter
- Check the value of the victim’s history.length
From the value of the history.length property the attacker can now tell whether or not the attack was successful, because the attacker knows that a successful login means that wordpress redirected the user to the page in the redirect_to parameter. Therefore if the value of the history.length property does not increase, he knows that the attack was successful. The attacker is also able to tell if a CSRF attack worked under certain conditions, which usually isn’t possible due to Same Origin Policy.
Proof of Concept Video
Below is a proof of concept video of how WordPress websites running on internal networks can be identified, even when running behind a firewall, and how then a bruteforce attack is launched against them.
Limitations and Problems of the WordPress Login Page Attack via XSHM
The Attack is Easily Noticed
In order for this WordPress attack to succeed the attacker needs at least two interactions from the victim:
- First he must convince the victim to visit his malicious web page.
- After that the victim must click a button or link on the attacker’s page that opens a new browser window or tab. This is required since it is not possible to open a new window or tab without user interaction, because of popup blockers.
Since the victim can easily notice the new opened tab and the page refreshes the chances of the victim not noticing the attack are very slim. Also, the attacker can’t just create a simple iframe as the wp-login page is secured with X-Frame-Options. This might cause problems in some web browsers since they might not increase the history.length value if this header is set, thus could be very difficult for an attacker to determine if there is a WordPress or not.
Different Browsers' Behaviour Complicates Matters
Another problem is that some browsers such as Chrome always change the value of the history.length property, even if the attacker redirects the iframe to its current src. This might be a counter measure for the XSHM attack, and in fact the attack will fail. So how can the attacker change the history.length without an iframe on the current page?
Using Window.Opener in the XSHM Attack
- Open a child window from his page, for example attacker.com/opener.html -> attacker.com/child.html
- In the child window the attacker uses the opener.history.length to retrieve the history length from attacker.com/opener.html
- Set the location of the opened window to http://192.168.1.123/wordpress/wp-admin/ using opener.location
- Set window.opener.location to http://192.168.1.123/wordpress/wp-login.php?redirect_to=http%3A%2F%2F192.168.1.123%2Fwordpress%2Fwp-admin%2F&reauth=1
- Set opener.location back to attacker.com/opener.html to be on the same origin again.
Now the attacker should be able to get the value of opener.history.length again and compare it to the one from step 2. This way the attacker can also bypass the X-Frame-Options protection against XSHM. This could also be stealthily done by using a popunder window.
The Maximum Value of the history.length Property
Another problem that might hinder these type of attacks is the maximum value of the history.length property. For example on Chrome its highest value can be 50. If the value needs to be increased and it is already at 50, the first (oldest) entry is removed and the last entry is added. This can be a problem when doing a Cross Site History Manipulation attack, but as a workaround the attacker can:
- Trick the victim into visiting a url from the same origin with window.opener.location.
- Then trick the victim again to navigate back to the first page he visited in the current session with window.opener.history.go(- (window.opener.history.length-1)). This first retrieves the amount of pages the user can go back and then goes back to the first page.
- Set the URL to a new link. The history value is 2 now.
This way the attacker bypasses the problem of the 50 entries limit.
Dealing with Logout CSRF Protection
Another hurdle for the XSHM attack is the logout CSRF protection. If the user is logged in the attacker usually can’t reliably check whether or not there is an actual WordPress installation on the server, so he can’t brute force the login page with a user that is already logged in.
Well WordPress is a little special in this case. When the victim visits wp-login.php he is greeted with a login prompt whether or not he is logged in. This would solve the problem the attacker would have with bruteforcing credentials, however it is still not possible to reliably check with wp-login / wp-admin if there is a WordPress installation on the web server. But WordPress has an additional parameter you can set to actually log you out when you visit wp-login. It is called reauth. When it is set to 1 you are automatically logged out, which means the attacker can try to point the victim to wp-admin and see if it redirects him to wp-login again.
How can You mitigate against the XSHM Attack?
As a WordPress user you can’t take any precautions to prevent XSHM attacks, since this is a browser feature you can’t control. You can only rely on the developers of the respective website to take all the necessary precautions that prevent XSHM attacks. These include:
- Avoiding conditional redirects that can leak sensitive information.
- Using of CSRF Tokens.
It can also be a good idea to add random characters to the URL. These don’t have to be connected to any application level logic, like CSRF tokens do, but can make it difficult for an attacker to guess the exact link where the victim will be redirected to.
Note: While there is a proof of concept for this WordPress attack it is unlikely to be used in a real life scenario because of the knowledge that is required about the target and because of the long time the victim has to spend on the attacker’s page, while having a refreshing window in plain sight.