Cross-Site Scripting WAF and Filter-Evasion Bypassing

This blog post contains information on tips and techniques to help bypass filters and Web Application Firewalls (WAFs) with Cross-Site Scripting (XSS) Payloads. We can leverage a simple pop-up to show a proof-of-concept in the examples. I will use DVWA, Kali Linux, and Burp Suite in this tutorial.

While I do not have a WAF setup to show for each demonstration or payload, the concept is to show how a payload can be exploited on a target application with a WAF in place and this should help give you ideas on how to attack an application and bypass protection.

Intro and Setup

At a high-level the way to think about bypassing a WAF, a filter, or any protection mechanism that’s preventing XSS is to find the right payload that can get around these protections and poor sanitizing practices.

It is common that applications for one reason or another, do not properly sanitize un-trusted input from the user and that applications rely on a WAF to protect them from attacks. An example can come in the form of a legacy application that doesn’t have the budget to fix code, but can stand up a WAF for protection.

Let’s get into the payloads.

Script Tag Blocking

Poorly configured WAFs that are looking to stop <script>alert()</script> attacks can be fooled by upper and lower case characters. It can be as simple as sCrIpT. Let’s try it out.

Payload: <sCrIpT>alert(‘xss’)</ScRiPt>

Depending on how you deliver your payloads, either through manual process into user input or fuzzing with something like Burp Intruder. It’s a good idea to try changing the case of the characters in your tags and payloads to try and get by a really poor WAF.

Confirm, Prompt, Print Payloads

The second tip will be to avoid using the alert tag. The alert payload is so common that WAFs will detect them and block the attempted request before it has time to be processed. Instead of using alert we can utilize either confirm, prompt, or print function calls to confirm XSS.

Let’s use the confirm function in this example.

Payload: <script>confirm(‘xss’)</script>

That is a pretty simple payload. Next we can try the print payload. The Print function will simply ready a page to be printed.

This technique can be very effective in bypassing a poorly configured WAF. Many fuzzing and pentesting tools validate alert() as a confirmed XSS discovery. Some WAFs simply analyze for an alert call and note it as malicious. They can block the request and in some cases even block the source IP.

HTML Payloads

Another technique is to use HTML tags such as <svg>, <img>, <audio> to display an alert for a POC (proof of concept) of XSS. The HTML targs <svg>, <img>, and <audio> are just a few examples. Tags such as <iframe>, video, <a> are other options. I will provide a cheat sheet below to reference.

In short, some WAFs will block <script>, but may be fine with any HTML tag. This is very common in my experience.

Payload: <svg onload=prompt(‘xss’)>

Many filters and WAFs are looking for <script> tags and it is simply better to attempt several payloads that do not use <script> and instead use HTML.

URL-Encoded Payloads

Another approach we can try is URL Encoding to obfuscate our payload and not use potentially filtered or blocked characters such as < or the > character. To URL encode your payloads you can use something like CyberChef or use Burp Suite Encoder option.

Payload: %3Csvg%20onload%3Dprompt%28%27xss%27%29%3E

Note* this decodes to <svg onload=prompt(‘xss’)>

Here we are sending URL-encoded data via the URL and it will usually be interpreted correctly by the back-end application. This means we can bypass protection blocking characters such as < and >.

HTML Entity Payloads

The last method will be encoding HTML elements, this is another form of obfuscating our payloads. HTML entities are characters that are reserved in HTML such as < which can translate to &lt; or &#60;.  

We can abuse certain protections and lazy developer sanitizing of un-trusted user-input by using HTML entities in our payloads.

Payload: <iframe srcdoc=&lt;script&gt;alert&lpar;’xss’&rpar;&lt;&sol;script&gt;></iframe>

Note* This decodes to <iframe srcdoc=<script>alert(‘xss’)</script>></iframe>


I just scratched the surface with a few examples of XSS WAF and Filter bypassing.

A great source I always have open during my pentesting is the Port Swigger XSS Cheat Sheet. This lists many payloads and their affected browsers. It also contains payloads for various Client-Side Template Frameworks to get alerts via VueJS or AngularJS.

OWASP also has a great page dedicated to XSS Filter Evasion.

If you found this helpful, please send me a tweet and tell me what you thought! Feedback is always appreciated!