XSS Doesn’t Live Here Anymore


Today, we will be taking a look at another reason to perform a web application penetration test: padding a business-case for upgrading end-of-life or legacy software. The company I worked for at the time needed to manage their telephone number inventory, while enabling legislative compliance. It was time to update the in-place solution as it was slow, outdated, and had some very specific requirements that made it very hard to work with, but no one wanted to foot the bill for the update.

While this story does involve a vendor, there will be no vendor-shaming. The software version under test was deemed end-of-life by the vendor, but the company was slow to update as the in-place solution was still technically working. You are likely to find vulnerabilities in any piece of software if you look long and hard enough. In this particular instance, a quick testing cycle found just the vulnerability needed: XSS on the login page.

The web application in question had a URL that looked like “http://example.com/Logon&PreviousPage=Logon”. The page was a basic-looking login page, but the login form where the user entered their credentials was a java applet. My first thought, already knowing that the stakeholders did not care about the fact that an out-of-date java version was required, was to look for XSS in that glowing PreviousPage=Logon parameter in the URL. Changing the value from Logon to another value caused that value to appear in the source code. Furthermore, any additional parameter added to the URL would be passed into the source code as well. For example, I added “&cats=meow” to the end of the URL, and this happened:

1-param-output
The cats=meow URL parameter reflected in the page’s code. Note that some of the data has been removed from this image for confidentiality.

Essentially, any parameter added to the page would appear as a PARAM in the source code! Now, if the page had input sanitization on the URL parameters, this might not matter so much. This, however, was not the case. Verifying my suspicions, I tried various special characters after the &cats=meow, like ‘ and “, finding that whatever I put into the URL was placed into the source code verbatim. Wonderful! We have found our way in!

Some thing that you need to know about JavaScript: if there is a JavaScript error on the page, JavaScript will generally just halt code execution all together. For cleanliness, it is important that all the HTML tags are clean as well. Because of this, I had to do some cleanup with my injection point. For starters, the parameter value was inserted into an HTML PARAM tag‘s attribute, and so I needed to break out of the attribute with a double quote (“). Next, I closed off the HTML PARAM tag with >, and closed off the JavaScript string with a single quote (‘). The JavaScript string was in an array, so I finished the array with a closing bracket. The array was in a function-call, so I closed that off with a closing bracket as well, and finished the statement off with the semicolon that JavaScript expects. After all this, my URL parameter looked like &cats=meow”>’));.

2-clean-up-start
Starting the clean-up.

Next up, I had to clean up the JavaScript that came after the HTML PARAM tag. This basically looked like the above clean-up, but in reverse. First, I made a call to a function, in this case void. I then repeated this void call instead of making an array, as that was completely valid in this case. Then, I started the JavaScript string again, open the HTML tag, and finish it off with a double quote. All in all, it looked like void(void(‘<“. In the end, the URL looked like /Logon&PreviousPage=Logon&cats=meow”>’));/*ExploitCodeHere*/void(void(‘<“. Note: the /*ExploitCodeHere*/ is a JavaScript comment, and is included in this snippet for clarity of where the exploit will be going in this vulnerability.

3-clean-js
All cleaned up!

This exploit could easily be tested, you just had to replace the /*ExploitCodeHere*/ text with a simple JavaScript alert()!

4-alert

An alert box popping up on a login page is not very scary to the big-wigs in charge of the big cheques, and so I started creating an actual exploit that could show off the actual risks. First, I completely removed the original login form with this code:

var x=document.getElementsByTagName('applet');
x[0].parentNode.removeChild(x[0]);

For some reason or another, I decided to build the new “evil” login form into an IFRAME on another host. I don’t remember my exact reasonings as it was some time ago, but in any case this method seemed to work fine. Another method would have been to dynamically generate all the form elements using JavaScript directly onto the login page, as we have the ability to execute JavaScript on this page already anyways because of the vulnerability. This is what the exploit looked like, broken down into a line-by-line:

5-iframe-code

For some reason that I also do not remember as it was a while ago, the URL had to be pieced together in that y variable as the JavaScript was error-ing out otherwise. After building a working exploit, I flattened it in the URL by removing the extra whitespace, and then was left with the following:

6-exploit-bundled
No more whitespace or line-breaks!

Thinking back on how this web application handles URL parameters and looking at the above URL, this code likely wont work on its own. The web app would likely get confused by the extra equal signs in the URL, maybe split these into their own HTML PARAM tags or just behave sporadically. There was an easy fix for this, and this little trick actually helps obscure XSS attacks from end-users during the social-engineering stage. All I had to do was URL encode the exploit and it was good to go. To take things further, and to further hide the attack from end-users, I completely URL encoded the exploit instead of just encoding the required characters.

7-encoding

To an end-user this might look suspicious, but it looks a lot less suspicious than the JavaScript code in the URL. Throw this into the end of the original “http://example.com/Logon&#8221;, load up the page and enter your credentials, and…

8-executed

…Bam! We now have the user’s credentials stored away for future (ab)use! For simplicity, I rigged the evil login page to display a warning message instead of storing the user’s password, but this gets the point across just fine. Of course, this version of the software was end-of-life and so no vendor patch would be coming, so the best course of action was to update to a newer version.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a website or blog at WordPress.com

Up ↑

%d bloggers like this: