This article explains how attackers can use the XSHM attack to identify WordPress websites running on internal networks and behind firewalls, and also launch a login bruteforce attack against them.
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.
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.
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.
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:
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.
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:
/wp-login.php?redirect_to=http%3A%2F%2Fexample.com%2Fwordpress%2Fwp-admin%2F&reauth=1
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.
The attacker can also use JavaScript to scan internal networks for websites running on WordPress. For example by using WebRTC, like implemented in the BeEF framework he can narrow down the list of live hosts which has to be checked for the above WordPress’ redirect. Once the scanning is done the attacker should have a list of internal IPs running WordPress. You can download a PoC of the JavaScript.
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 cross-site request forgery (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:
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.
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.
In order for this WordPress attack to succeed the attacker needs at least two interactions from the victim:
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.
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?
The answer is window.opener. If a new browser window or tab is opened from another tab, either by clicking a link or with javascript, the new page can access its parent’s window object. It is even possible to get the value of the history.length property if the page is from the same origin. At first this does not seem very useful, since the attacker needs to know the value of history.length property after redirecting to a cross origin page to carry out the XSHM attack. But since the attacker can set the location of the parent window, even via cross-domain he can do the following:
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.
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:
 This way the attacker bypasses the problem of the 50 entries limit.
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.
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:
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.