They Said the WAF Was Impenetrable Until I Executed This XSS Payload + Video

Listen to this Post

Featured Image

Introduction:

Web Application Firewalls (WAFs) are the first line of defense against common web attacks, but they are not a silver bullet. As demonstrated in a recent penetration test, blacklist-based filtering can be systematically evaded, allowing dangerous Cross-Site Scripting (XSS) vulnerabilities to be exploited. This deep dive explores the methodology of bypassing WAFs by enumerating allowed HTML event attributes, transforming a filtered input into a fully executed JavaScript payload.

Learning Objectives:

  • Understand the limitations of blacklist-based WAF filtering for XSS prevention.
  • Learn the step-by-step methodology for enumerating and testing allowed HTML event attributes to bypass filters.
  • Gain practical knowledge of using Burp Suite Intruder for systematic payload testing and evasion.
  • Develop mitigation strategies that move beyond simple filtering to proper output encoding and context-aware security.

You Should Know:

  1. The Anatomy of a Blacklist Bypass: Beyond ` payload. Modern WAFs instantly block this. The evasion begins when you realize the WAF is only filtering a predefined list of HTML tags and perhaps a handful of obvious event handlers like `onclick` or onmouseover. The core vulnerability lies in the application's failure to properly encode user input before rendering it in the browser context. The attacker's goal shifts from using blocked tags to discovering which event attributes, HTML elements, or encoding schemes the WAF overlooks.

Step-by-step guide:

Step 1: Initial Reconnaissance

Inject a basic payload and observe the WAF response. Use a proxy like Burp Suite to intercept the request.

GET /search?q=<script>alert(1)</script> HTTP/1.1

If blocked, note the error (e.g., 403 Forbidden, or a custom "Blocked by WAF" page). This confirms blacklisting.

Step 2: Probing with Lesser-Known Tags

Test with alternative tags that might execute JavaScript without being on the blacklist.

GET /search?q=<img src=x onerror=alert(1)> HTTP/1.1
GET /search?q=<svg onload=alert(1)> HTTP/1.1
GET /search?q=<body onload=alert(1)> HTTP/1.1

Monitor which, if any, return a 200 OK response, indicating the tag/attribute pair wasn't blocked.

2. Systematic Enumeration with Burp Suite Intruder

When initial probes fail, a systematic approach is required. The key is to test a vast array of HTML event attributes to find those the WAF rule set ignores.

Step-by-step guide:

Step 1: Prepare the Attack

In Burp Suite, send a request with a placeholder to the Intruder tool (e.g., GET /search?q=<svg §TEST§=alert(1)>).

Step 2: Build a Payload Wordlist

Create or use a comprehensive list of event handlers. Example snippet:

onload
onerror
onclick
onmouseover
onfocus
onblur
onanimationstart
onbeforescriptexecute
onpointerrawupdate
onbeforeinput
...

Include obscure, draft, or vendor-specific events (e.g., `onwebkitmouseforcechanged`).

Step 3: Configure and Run Intruder

Set the attack type to "Sniper." Place the payload position at the `§TEST§` marker. Load your wordlist as the payload. In the "Options" tab, add a Grep Match rule for a substring of your success message (e.g., "alert") or monitor response lengths—a different length often indicates successful injection without a block.

Step 4: Analyze Results

Sort responses by length or status code. A response that is the same length as a benign request but contains your `alert` payload in the HTML source code indicates a successful bypass. For instance, you might find that `onbeforeinput` or `onpointerenter` is not filtered.

3. Crafting the Final Exploit Payload

Once you identify an allowed event attribute, you must craft a functional exploit. The payload must be contextual. If injected into an attribute value, you might need to break out of the attribute first.

Step-by-step guide:

Scenario A: Direct Injection into HTML Body

If input is reflected directly inside the page body, your winning payload from Intruder might work as-is:


<

svg onpointerenter=alert<code>1</code>>

Note: Using backticks for `alert` can sometimes bypass further character filters.

Scenario B: Injection into an Existing HTML Attribute

If input is reflected inside an existing attribute like value="USER_INPUT", you must close the attribute and tag.

">

<

svg onbeforeinput=alert(document.domain)>

This closes the preceding quote and tag, then introduces your malicious element with the bypassing event handler.

4. Advanced Evasion: Encoding and Obfuscation

If the WAF decodes input before checking it, simple encoding can bypass filters. This is a cat-and-mouse game of finding which transformations are not normalized.

Step-by-step guide:

Step 1: Try HTML Entity Encoding

Does the WAF decode `>` to `>` after filtering? Test:

GET /search?q=<img src=x onerror=alert(1)> HTTP/1.1

Step 2: Try JavaScript Encoding

If output is within a `