This is the second post in a series on cross site scripting(XSS). In this entry I examine cross site scripting and the way that it can creep into our programs and what different cross site scripting input strings look like. Once we develop a deep understanding of XSS, I’ll show you how to exploit XSS vulnerabilities in my next post before wrapping up with how to defeat input filters & remediation to cross site scripting vulnerabilities. You can see the first post in the series here.
I have provided another tutorial web application so that you can follow along with this post, the web application which is functioning and usable can be found here.
Cross Site Scripting
There are essentially 5 main input areas where we can introduce cross site scripting vulnerabilities. They are:
- Hidden Fields on a Form
- Session Storage
- Cookie Storage
- Injected Files
- Injected Cookies
Hidden Input Field
The hidden input field on an html form is used for storing input that’s well hidden from the user. Essentially what the first example in the demo does is set a hidden input and post back, on the post back the hidden form value is read and displayed to the user. When used correctly everything works as expected and indicated from the image below.
The attack string we’ll use to demonstrate this vulnerability is:
However when this input is used for malicious purposes & the cross site scripting vulnerability is discovered look at the outcome from the image below. To try this example below yourself input the following string into
the text box.
Now lets examine the code that causes this vulnerability.
Response.Output.WriteLine("Your input from exmaple was: " + m_hdnInpt.Value);
It’s obvious to see what the problem is here, we are populating a hidden input on client side, with the value of the user input and then blatantly writing the value back into the raw HTML on the server to be redisplayed to the client. Note that this is not detected by ZAP as a cross site scripting vulnerability. The server should not just trust the input coming from the client.
In this example, if you want you can first test on the demo application that normal input works as desired, I know it does so I am going to move straight to the vulnerability. The difference with between the first example and this example is that, in the first example the form posted back to Default.aspx, in this example the form redirects to SessionInpt.aspx, this example and the remaining examples are similar to the first post in this series were data is being passed between two pages.
The attack string I am going to use for this example is:
Notice from the image below, that the cross site scripting vulnerability was successfully exploited.
Now lets examine the code:
Session["SessionInpt"] = m_sessionInpt.Text;
Response.Output.WriteLine("Your input for exmaple 2 was: " + Session["SessionInpt"]);
As you can see Default.aspx blindly throws the data from the user input into a session variable, which SessionInpt.aspx gladly accepts, trusting that everything in the session is safe. If I were in a leadership position reviewing this code, I would be upset that Default.aspx did not do any input validation, I would be even more upset that SessionInpt.aspx did not do any input validation and just trusted the input. This type of a situation can result if you have two devs working on these pages separately each trusts the other to handle security. This is also very good justification for performing a security review. Having validation on both pages is best and builds on a concept of defense in depth.
This example is one of my favourites, and the reason being is because of the many ways that it can actually impact the user. Up until this point the previous two examples and the previous post were essentially a 1 time cross site scripting attack. In this example we’re actually going to store the attack in a cookie.
The attack string we’ll use for this attack is:
As you can see from the image below the exploit is successful.
The more disturbing trend here is because this exploit is saved in a cookie everytime the user visits the page they will re see the exploit. If you’re following along in the demo application I would encourage you to enter the attack string on Default.aspx. When you’re redirected, copy the url that you’re redirected too, in your URL bar navigate to a different url say google. Once Google loads, past the redirected url that you copied back into your browser. Notice how the exploit immediately reappears and will continue to reappear until you clean your cookie. If you would like to clean you cookie and try again, selected the delete cookie button.
Lets examine the code:
Response.Cookies["UserInpt"]["Data"] = m_txtCookieInpt.Text;
String txt = Request.Cookies["UserInpt"]["Data"];
Response.Output.WriteLine("Your input for exmaple 3 was: " + txt);
As you can see, Default.aspx, drops a cookie when the button is clicked, that CookieInput.aspx is going to pick up and constantly read from the cookie for exploits.howellsonline.ca until the cookie is cleared, therefore the user will constantly see this exploit, and it may drive them away from your site.
For this example our attack string will be:
<p>Look at the output shown below:</p>
String txt = (String)Session["Injected"];
Response.Output.WriteLine("Your input for exmaple 4 was: " + txt);
There is one more important file that we need to look at and that, was the script we injected which can be found here
document.writeln("Hello site you have an injected suffered an XSS attack");
This attack was pretty simple essentially I was able to get my injected script to run. The reason that this was possible was because the Redirected page blindly accepted input from Session, and immediately wrote that input to the HTML without verification. When it did this and the page rendered, the page went off and made an HTTP GET request for the injected url, as soon as the requested script was received the script executed and defaced my web page. Interestingly enough up until this point, all of the other attacks that we’ve seen have displayed a message box, however this script does not display a message box and if it were not for the output the user wouldn’t know that this script had executed at all.
This next example demonstrates the combination of a couple of attack vector’s we’ve already seen, in this example we are going to inject a script into a cookie, every time the page loads the page will download the script as in the previous example.
The attack string we’ll use for this attack is:
<p>As you see the attack is successful.</p>
It would stand to reason then that as long as you don’t use this pattern that you’re reasonably okay. Up until this example you would be correct.
Lets’ look at the code:
Response.Cookies["UserInpt"]["Deface"] = TextBox1.Text;
Notice the difference here? The difference is that the cookie is read from the client when the HTML body loads. So instead of the vulnerability being made in the back end server side code, it’s actually all happening on the client, therefore the request does not have to back to the server and come back to the client.
I hope the moral of the story is do not trust unvalidated user input. I’ve demonstrated 5 sources that user input can come from, all of it as you see should be properly validated before being displayed and shown to the user. I would even recommend validating the input both on input and before you should it to the user, however as a minimum it should be validated before being written to the html.
In my next post I’ll show you how to exploit these vulnerabilities, and then how to re mediate them and how potential attackers will attempt to by pass your filtering and what you can do about it.