Cross-site scripting can be one of the easiest vulnerabilities to discover, but to be successful with this type of attack, it is essential to learn how to get past filters. In the previous guide, we explored some ways to do this, such as abusing attributes and event handlers and tricking the application into accepting unusual characters. Now, let's take a look at more techniques used to defeat filters.
Existing JavaScript & Character Escaping
There is no doubt that scripting languages like JavaScript have dramatically increased the functionality of web technologies, allowing for efficient client-side processing, dynamic content generation, and a slew of other things that make life easier for both developers and users. But with these enhancements come more attack vectors for a determined hacker, especially when it comes to cross-site scripting.
Let's suppose an application takes a string submitted by the user and processes it in some way via JavaScript, like defining the string as a variable for later use. A simple example:
<script>var a = 'myteststring; ... </script>
If we can inject a payload, we can determine if this particular avenue of attack is vulnerable. By closing the single quote, terminating the statement with a semicolon, and inserting our desired code, we can do just that.
'; alert(1); //
Here we have ensured that the script will still function properly by commenting out the rest of the script with double slashes. We could also declare another variable and insert another opening quote, since we terminated the string earlier, and it should behave in much the same way.
Often, an application will block the use of specific JavaScript keywords and characters, preventing the attacker from using methods such as the one above to perform XSS. JavaScript allows the use of character escaping, which can sometimes be used to bypass filters and get around this problem.
One method is to use the escape character to escape the imposed escape character, like so:
<script>var a = '\\'; alert(1); //
This essentially un-escapes the blocked quote and allows the payload to execute. Another method is to utilize Unicode characters, which also allows the script code past certain filters.
<script>a\u006cert(1)</script>
Dynamic String Construction & Eval()
JavaScript contains a function called eval() which evaluates any given expression as a string. For example, the following would evaluate to 2.
eval('1 + 1')
If the application allows the use of this function, it can be used to deliver an XSS payload. Like before, we can use Unicode encoding within the string to sneak past the filter.
<script>eval('a\u006cert(1)')</script>
If the eval() function is blocked, which it often is, success can sometimes be found by encoding the actual characters of the function as well.
Another technique we can use is dynamic string construction within the eval() command.
<script>eval('al' + 'ert(1)')</script>
There is another function that can be used in lieu of eval() called fromCharCode(). This will construct a string from individual characters, which can be successful when more direct methods are being blocked.
<script>eval(String.fromCharCode(97, 108, 101, 114, 116, 40, 49, 41))</script>
Character Encoding
It's often useful to try a multitude of character sets when trying to beat filters. By encoding some or all of the desired payload, filters can sometimes be tricked into allowing input that would otherwise be blocked and discarded. Some encoded examples of the string "alert(1)" follow.
Hexadecimal:
61 6C 65 72 74 28 31 29
Octal:
141 154 145 162 164 050 061 051
Binary:
01100001 01101100 01100101 01110010 01110100 00101000 00110001 00101001
Base64:
YWxlcnQoMSk=
Encoding can be utilized on particular characters, strings, and everything in between. In some instances, different character sets can even be combined to bypass defenses, so it pays off to experiment and explore all the possibilities pertaining to encoding when testing for XSS.
Meta Refresh & File Renaming
A web browser can automatically refresh the current page after a certain period with something called meta refresh. This is employed by setting a parameter in the meta HTML element. Meta refresh has been known to send the header without a referrer, so this can be an avenue of attack when referring URLs are not needed. We can utilize the pseudo-protocol technique that we covered earlier to inject our payload.
<meta http-equiv="refresh" content="0;url=javascript:alert(1);">
Another odd occurrence that can sometimes be exploited is the way file types are filtered. If an application is blocking any JavaScript files (.js), try renaming the source to an image file.
<script src="payload.jpg">
Sanitization & Length Limits
Sanitization is arguably the most common type of defensive filter encountered when performing XSS attacks. This process attempts to strip or encode some aspects of the payload in an attempt to render the code harmless or stop it from running correctly.
Sometimes an application will try to remove the first instance of a script tag. In these cases, this defense can often be overcome by using multiple tags.
<script><script>alert(1)</script>
The first tag gets stripped leaving the resulting code to execute as intended. In other situations, we can check if the filtering is done recursively.
<sc<script>ript>alert(1)</script>
Here, if the script tag is removed, the resulting code still works after being joined together. When it comes to beating sanitizing filters, diligence pays off. It is vital to discover precisely how the sanitization is being performed and what is being filtered to outsmart this defense method.
Another type of defense commonly encountered when probing for XSS flaws is input truncation. There's not much you can do when there is limited space to craft a payload, so depending on the situation, one has to devise clever ways of getting around this predicament.
One such situation arises when there are multiple elements of user input being returned on the same page. Let's suppose there is a page that contains the following elements:
<input type="hidden" name="id" value="54">
<input type="hidden" name="checksum" value="345123">
<input type="hidden" name="status" value="critical">
By spreading the payload across these three elements, we can effectively bypass the length restrictions in place for each element on their own.
<input type="hidden" name="id" value=""><script>/*">
<input type="hidden" name="checksum" value="*/alert(1)/*">
<input type="hidden" name="status" value="*/</script>">
The pieces of code between the comment marks (/* and */) get ignored, so the browser ultimately processes our payload as if it was injected in only one location, thus defeating the length limits imposed on each element.
Wrapping Up
XSS is one of the most common vulnerabilities on the web today, and as such there are no shortages of defense against this type of attack. Throughout this guide, we have learned a variety of methods that can be used to bypass filters and deliver a successful attack. Though we have covered a multitude of techniques, the best way to defeat defensive filters is often a combination these. All it takes is a little time, persistence, and ingenuity to be successful.
Just updated your iPhone to iOS 18? You'll find a ton of hot new features for some of your most-used Apple apps. Dive in and see for yourself:
Be the First to Comment
Share Your Thoughts