WAF Bypass

What is a WAF?

A Web Application Firewall sits between the internet and the web application, inspecting HTTP traffic in both directions. It blocks known attack patterns like SQLi, XSS, and command injection based on rules.

How WAFs work:

  • Rule-based - predefined patterns. If a request contains SQL syntax, block it
  • Heuristics - behavioral analysis over time. 10,000 requests with integer input, then a string input suddenly = suspicious
  • Threat intelligence - real-time updates about new attack signatures

The key weakness: WAFs require constant tuning and maintenance. Most organizations deploy them once and forget. This means even when a WAF is present, it is often misconfigured or running outdated rules - weaker than it could be.


Step 1 - Detect the WAF

wafw00f

wafw00f https://TARGET

Identifies the WAF solution if possible (F5, Cloudflare, ModSecurity, Akamai, etc.). If it can’t identify the vendor it still tells you a WAF is present.

If wafw00f can’t identify it, look at the error response manually:

  • Send a known-bad payload (e.g. <script>prompt(1)</script> in any input field)
  • Normal input 200 OK
  • Bad payload 403 Forbidden with a custom error page

Custom error pages are often branded by the WAF vendor. The error message or response headers (X-Sucuri-ID, CF-RAY, X-Powered-By-Plesk) can fingerprint it.


Step 2 - Find What Gets Through

Before trying clever bypasses, fuzz to see which payloads actually get blocked. Use Burp Intruder:

  1. Send a request with a payload parameter to Intruder
  2. Load an XSS or SQLi wordlist
  3. Run the attack
  4. Sort by status code - look for 200s among the 403s
  5. Those 200s are payloads that slipped through

Important: WAFs block IPs after too many bad requests. Be targeted, not noisy. Use a smaller, curated wordlist rather than throwing thousands of payloads.


Step 3 - Bypass Techniques

Case variation

WAFs match exact patterns. Changing case can bypass naive rules:

sElEcT * fRoM users
UNION SELECT -> UnIoN SeLeCt

Comment insertion (SQL)

Break up keywords with comments the SQL engine ignores but the WAF doesn’t expect:

SE/**/LECT * FR/**/OM users
UN/**/ION SE/**/LECT null,null,null
/*!UNION*/ /*!SELECT*/ null,null

Whitespace alternatives

Replace spaces with characters the WAF doesn’t account for:

Tab:        SELECT%09*%09FROM%09users
Newline:    SELECT%0A*%0AFROM%0Ausers
Carriage:   SELECT%0D*%0DFROM%0Dusers
Form feed:  SELECT%0C*%0CFROM%0Cusers

Encoding

  • URL encoding: < = %3C, > = %3E, ' = %27
  • Double URL encoding: % = %25, so < = %253C
  • HTML encoding: < = &#60; or &#x3c;
  • Base64 + atob(): encode your JS payload in base64, decode and execute it:
<script>eval(atob('YWxlcnQoMSk='))</script>

The WAF sees base64 gibberish, the browser decodes and runs it.

Filter bypass for script tag filters

If the WAF strips <script>:

  • Switch to event-based payloads: <img src=x onerror=prompt(1)>
  • Try: <svg onload=prompt(1)>, <body onload=prompt(1)>
  • Test if the filter is recursive:
<scr<script>ipt>prompt(1)</scr</script>ipt>

If the filter removes <script> once but not recursively, the outer tags survive and form the complete payload.

sqlmap tamper scripts

sqlmap has built-in tamper scripts specifically for WAF bypass:

sqlmap -u "URL" --tamper=space2comment
sqlmap -u "URL" --tamper=between
sqlmap -u "URL" --tamper=randomcase
sqlmap -u "URL" --tamper=charencode
sqlmap -u "URL" --tamper=base64encode
sqlmap -u "URL" --tamper=space2comment,randomcase,between

Chain multiple tamper scripts with commas. List all available: sqlmap --list-tampers

HTTP-level bypasses

  • Parameter pollution: send the same parameter twice - WAF checks the first, app uses the second
?id=1&id=1 UNION SELECT null--
  • Chunked transfer encoding: split the request body into chunks - some WAFs inspect each chunk independently and miss payloads split across chunks
  • Content-Type switching: if the app accepts both JSON and form-data, try switching - WAF rules may differ per content type
  • HTTP verb tampering: if POST is blocked, try PUT or PATCH

Step 4 - Context Matters

WAF bypasses are situational. What works against one WAF configuration will not work against another. There is no universal bypass. The transcript makes this clear: don’t trust “WAF bypass” posts on social media - test them against the actual target.

When you find a character or keyword being filtered, use the filtering playground approach:

  1. Identify exactly which characters/keywords are stripped
  2. Find payloads that achieve the same effect without those characters
  3. Test encoding variations of the same payload
  4. Check whether filtering is recursive or applied only once

Quick Reference

TechniqueUse case
wafw00fDetect and fingerprint WAF
Case variationBypass keyword matching
Comment insertionBreak SQL keywords
Whitespace alternativesBypass space-based rules
URL / double encodingBypass pattern matching
Base64 + atob()Obfuscate JS payloads
Event handlers instead of script tagsBypass script tag filters
Recursive filter checkFind non-recursive filter implementations
sqlmap --tamperAutomated SQLi WAF bypass
Parameter pollutionWAF sees one value, app uses another

Resources

Testing Checklist

  • Detect and fingerprint the WAF: run wafw00f https://TARGET - it identifies the WAF vendor or at least confirms a WAF is present
  • Manually confirm the WAF: send a known-bad payload (e.g., <script>prompt(1)</script>) and compare the response to a normal input - look for 403 Forbidden, custom error pages, or WAF-branded responses
  • Check error response headers for WAF fingerprints: X-Sucuri-ID, CF-RAY, X-Powered-By-Plesk, etc.
  • Fuzz with a curated wordlist in Burp Intruder to find which payloads get through (200s among the 403s) - use a smaller list to avoid getting your IP blocked
  • Test case variation to bypass keyword matching: sElEcT, UnIoN SeLeCt, ScRiPt
  • Test comment insertion for SQL: SE/**/LECT, UN/**/ION SE/**/LECT, /*!UNION*/ /*!SELECT*/
  • Test whitespace alternatives: tabs (%09), newlines (%0A), carriage returns (%0D), form feeds (%0C) instead of spaces
  • Test encoding bypasses: URL encoding, double URL encoding (%253C), HTML encoding (&#60;), Base64 with eval(atob('...')) for JS payloads
  • If <script> tags are filtered, switch to event-based payloads (<img src=x onerror=prompt(1)>) and test if the filter is recursive (<scr<script>ipt>)
  • For SQLi behind a WAF, use sqlmap tamper scripts: --tamper=space2comment,randomcase,between - chain multiple with commas, list all with sqlmap --list-tampers
  • Test HTTP-level bypasses: parameter pollution (same param twice), Content-Type switching (form-data vs JSON), HTTP verb tampering (POST to PUT/PATCH)
  • Remember: WAF bypasses are situational. What works against one configuration will not work against another. Identify exactly which characters/keywords are blocked, then craft payloads that achieve the same effect without them
  • Include in report: the WAF identified, the blocking behavior observed, the bypass technique and payload used, and the resulting vulnerability (SQLi, XSS, etc.) that was exploited through the bypass