In today’s post I am going to show a real-world example of stealing someone’s WordPress credentials using XSS exploitation, and getting shell access to the underlying host.
I chose this topic because of the general misconceptions I have seen around the potential effects cross-site scripting (XSS). In addition, I find the usual pop-up alert(1) window inadequate to demonstrate the potential consequences of XSS to non-security people.
So I am going to show a complete walk-through process of owning a WordPress site and its hosting environment. We are going to exploit a recent WordPress bug in order to exfiltrate data from the site.
Let’s set up a dummy site running WordPress v2.8.6 on an unpatched Ubuntu 12.04 LTS machine. It simply represents a regular blog site running slightly outdated software.
As you can see, this is just a regular blog that allows visitors to comment on the posts. This is what we are going to exploit below. My goal is to present a fake login page to the WordPress administrator, and steal the username and password combination that he or she enters.
The attack vector I chose is a recent WordPress bug (CVE-2014-9031), that affects all WordPress releases up to v3.9.3. If you are interested in a detailed explanation of the vulnerability, check the reporter’s website for more details.
Preparing the Exploit
Our first step is take the original exploit and modify it for delivering our malicious code instead of popping up the boring alert(1) window.
As a reminder, this is the original proof-of-concept from the original reporter (you must enable scripts from pastebin.com to view it):
Line 2 will replace the contents for the Edit Comments page under the admin dashboard with our fake login page. Lines 3 and 4 meant to remove the footer to make our login page appear genuine. The last bit adds a cookie to the victim’s browser, as showing the login page over and over every time visiting the Edit Comments might raise suspicions. So Lines 1 and 5 are adding a cookie to the browser, which will prevent the fake page showing up more than once ever 10 minutes. Note: The reason why I am not replacing everything between the <body></body> tags is some limitations (drop a comment for the technical details if you are interested). So we are replacing the contents between <div id=”wpwrap”></div> instead.
Adding the Payload
The next step is to craft the payload that is going to be inserted into line 2. The following HTML code mimes the standard WordPress login page prompting for the username and password.
Line 27 defines where the stolen credentials should be sent in a hidden iframe. This is a non-existent URL for now, but I will come back to this later.
Delivering the Exploit
Now let’s Base64 encode the HTML payload (I will leave up to you this conversion), and replace the placeholder text in the exploit. The final minified code that goes into the comment field of the WordPress post is the following:
The code is ready for exploitation! Now visit one of the victim’s post, and paste the exploit above as a new comment. The malicious code becomes an integral part of the page, ready to trick the victim.
Here Comes the XSS
The final part is when the WordPress administrator logs into the dashboard for moderating user comments. Normally this page looks like the following.
However, one of the listed comments contains our malicious code this time. As a consequence, the exploit is executed by the user’s browser once he/she navigates to Edit Comments.
So the sneaky code swings into action and overwrites Edit Comments with the fake login page from the payload. Notice the URL bar is intact, and the “Session Expired” text might probably make this login prompt genuine.
If the WordPress administrator enters his username and password here, his/her credentials are forwarded to the attacker’s website via a HTTP POST method (refer to line 27 in the payload for the URL). We used a hidden iframe (line 27 and 51) because the URL bar will not change in this case.
We can take a quick glance on the DOM of the modified page in the meantime. The highlighted line shows that the contents of the login form are sent to Mallory’s server using the POST method.
In addition to that, a cookie is added to the victim’s browser to prevent further pop-ups for the next 10 minutes.
After the credentials are sent and the cookie is set, the victim is redirected back to the Edit Comments page by collect.php. Because the cookie was added earlier, our exploit does not activate, so the page is loaded as usual this time. The exploit resides in Mallory’s comment on the top.
The administrator can manage the post’s comments as usual. However his/her credentials has been exposed to the attacker over the fake login page. The following screenshot shows the stolen credentials on the attackers machine saved by collect.php.
If we open the cookie jar, we can verify the presence of the “visited=true” cookie that prevents further fake login pages for the next 10 minutes.
This post is the first part of a series of articles. In the next part, I am going to show how collect.php works, and other sneaky methods for collecting the stolen credentials. Later on, we are going to use the WordPress administrator account to get shell access to the underlying server. Finally we are going to dump the contents of the database, elevate our privileges to superuser, and use the compromised host as a pivot to compromise other systems.
This is the first part of a series demonstrating total data exfiltration by exploiting XSS