I apologize ahead of time if I start to ramble through this post. Script injections are major vulnerability in web applications due to the variety of attacks that can result from one injection point and there’s a lot we can talk about. If we take a look at the Verizon Data Breach Investigation Report, we see the Cross-Site Scripting was involved in 6.3% of web application data breaches last year. This number is obviously higher in industries that have a higher number over web app data breaches, such as the financial services or information sectors. But if you’re new to the cyber security/penetration testing field, these will be the bane of your existence. No matter how many times you describe remediation techniques to your development teams, they will always find a way to let a couple of well-designed scripts through. The WhiteHat Website Security Statistics Report shows that 47% of applications are vulnerable to Cross-Site Scripting, making it the third most common form of vulnerability behind information leakage (56%) and insufficient transport layer protection (70%). Before I bore you with any more statistics, let’s dive into some fun testing methods.
I’ll be honest, if you and your team utilize an automated scanner, it will do the majority of the work for you. Most of the scanners that I’ve used a highly reliable in identifying injection points (AppScan and Burp Suite Scanner are great, OWASP ZAP is ehh). But that doesn’t mean you can’t make some fun proof-of-concepts for your report. Let’s start with the basics, but feel free to skip ahead if this is old news. I follow a couple of steps when doing my testing:
- Identify the reflected parameter
- Analyze any type of input validation
- What symbols are rejected/stripped/encoded?
- Are certain tags rejected?
- Will it reject action events?
- Are regular expressions used to strip certain content?
- Trial and Error
- How do we bypass any restrictions?
- How do we build on our base injection?
All of the examples I’m going to show were tested on IBM AppScan’s test site which can be found here. If you want to reproduce any of the tests or do you own tests, it’s a great site to experiment with.
Identifying Reflected Parameters
Some parameters will be obvious when they’re reflected, and some will be completely hidden. Over time and with more experience you’ll start to learn where to look. In our test site, there is a parameter that is clearly reflected back into the response when using the search feature:
Pretty hard to miss that. Now that we’ve identified a parameter, we can start trying to identify if there’s any protection already in place (Spoiler alert: there isn’t). So let’s try to inject the most basic of all script injections alert(1):
Analyzing Input Validation
The name of the game is trial and error. Burp Suite’s Repeater will be your best friend, and Intruder can do some of the heavy lifting. I’d recommend familiarizing yourself with these tools, and I plan on covering Burp Suite in a separate post (there’s just SO much functionality to cover). The key is to think like the development team. What is happening on the back end to stop you from injecting your script? Let’s go through some examples of poor input validation and how to dodge them.
Note: In these examples, we are assuming that the validation solution is application wide. There are some cases where there may be different methods of validating input across different parts of the application. This is far from ideal, and it would be worth noting to the development team.
Example 1: Tag Blacklisting
Example 2: Stripping Using Regular Expressions
This is just a very basic example. The text highlighted in blue is the matched text that would theoretically be removed by the input sanitization code. Here’s a slightly more complex regular expression that covers more ground:
If you happen to come across this type of input validation, your job is to figure out what type of regular expression is being used in the background. Two good places to start are checking if the expression is case sensitive and checking if the function is recursive.
Example 3: Character Encoding
Proper character encoding is the first step to effectively protecting applications from injection attacks. By encoding certain special characters that could potentially be used to inject malicious content, we can nullify any attacks. The following characters should be entity encoded:
If you’re assessing an application that is using HTML entity encoding, be sure to check that all characters are properly encoded. Some application may only encode the less than and greater than symbols. This still also injections into HTML attributes. Also be sure to check that the validation solution is consistent across the entire application. I’ve come across several applications that had spotty input validation which allow certain scripts to be injected in some parts of the application but not others. This should be an issued raised to the development team and fixed as soon as possible.
There’s much, much more to say about proper encoding, so I’ll direct you to OWASP’s Cross Site Scripting Prevention page. As usual, OWASP does an exemplary job of detailing exactly what needs to be done to properly protect your applications.
Proof of Concept
Now that you’ve found some injection points, we can make some fun proof of concepts to present. Alert boxes definitely prove the vulnerability exists, but showing how it can be exploited shows how dangerous they can be. I’ll go through a few of my favorite examples.
Fake Data Input
Remember that you can always inject a little extra HTML code to support your script injection. The following example injects a fake form telling the victim that if they enter their credit card number, they’ll be entered for a prize. Of course, there is no prize, and their card number is sent to an attacker controlled domain (in this case, Google) using an HTTP GET request. The code is shown below:
When inserted into our injection point, the result appears as follows:
Not totally convincing, but with the right phishing email and a little more work it could be devastating. The same concept can be applied by injecting a fake login screen or a fake check out screen.
In this example we will use a script to facilitate the download of malware to the victim. The script is simple, just using the document.open function to open a new window with our download URL. Don’t worry; it’s not actually malware, just pictures of cute puppies. The code looks like this:
Pretty simple, right? That bit of code results in the following:
The download happens automatically and appears as if it is coming from the banking site since it popped up when we navigated there.
And the result looks like this:
That’s all I’m going to cover in what is most likely my longest blog post to date. Hope you enjoyed and feel free to ask questions, make suggestions, or even share your own proof of concepts. Thanks for reading!