← Blog
// RED TEAM · WEB

How a WAF gets bypassed (and why a blocklist is rarely enough)

Published Aug 12, 2026 · 8 min read
Red TeamWebWAFEvasión

"We have a WAF" reassures people more than it should. A well-placed Web Application Firewall stops the noise and automated scanners, but most run on a blocklist: they block "known-bad" strings. And a blocklist chases text, not intent. Since there are infinite ways to write the same attack, there is almost always one the list didn't foresee.

What a WAF is (and isn't)

A WAF inspects requests and blocks those matching its rules. Two philosophies: blocklist (deny known-bad — most of them) and allowlist / positive security (allow only what's expected — safer, harder to maintain). The blocklist is a speed bump: it slows you, it doesn't stop you. Here are the four bypasses you see again and again.

1. Same attack, different form (XSS without <script>)

The classic mistake: block <script> and think you stopped XSS. But JavaScript runs in many ways. An on… attribute on any tag does the job:

Blocked:  <script>alert(1)</script>
Passes:   <img src=x onerror=alert(1)>
Passes:   <svg onload=alert(1)>

The rule chased one specific tag; the attack (running JS) has dozens of routes.

2. Encode what's forbidden (path traversal)

The WAF blocks raw ../, but the server decodes %XX before using the path. Encode the dots and slashes and the filter won't see the pattern while the server rebuilds it:

Blocked:  ../../etc/passwd
Passes:   %2e%2e%2f%2e%2e%2fetc%2fpasswd

The same idea sneaks "forbidden" characters into almost any parameter.

3. Break the pattern (case, comments, no spaces)

Many SQLi rules are naive: they look for lowercase or with spaces, or the word UNION. They break by changing the form without changing the meaning:

Blocked:  ' OR 1=1 --
Passes:   '/**/OR/**/1=1        (inline comments as separators)
Passes:   'oR'1'='1'            (no spaces, string comparison)

Mixed case, /**/ comments, tabs instead of spaces… all point to the same thing: the SQL engine is tolerant; the filter is literal.

4. Double encoding (against the "smart" WAF)

Some WAFs decode once and then filter. The answer is to encode twice: the WAF sees a clean layer, and the app, decoding again, reveals the attack.

%3C          -> "<"            (one layer: the WAF blocks it)
%253C        -> "%3C" -> "<"    (two layers: WAF sees %3C, app sees <)
🧱 You can practice all four in the WAF Bypass Lab: four levels where you write the payload and the engine tells you whether it passes the filter and meets the goal. To generate variants of any payload, the WAF Bypass mutator; and for the encoding angle, "encoding is not encryption".

Why there's always a gap

Because a blocklist tries to enumerate the infinite. The attack is an intent (run JS, read a file, alter a query); the representations of that intent are countless (encodings, case, comments, alternative characters, fragmentation…). The defender must catch them all; the attacker needs just one.

⚠️ The WAF isn't the fix, it's a layer. If your only defense against SQLi is the WAF, you have SQLi. The WAF buys time (virtual patching, stops scanners, blocks the obvious); it doesn't close the hole.

What actually closes the hole

Checklist

A WAF is a good guard at the door, but it doesn't repair the broken lock inside. Use it for what it is —a layer— and fix the flaw where it really lives: in the code.

Share: LinkedIn X
Sergio Belmonte Morales
Sergio Belmonte Morales
Cybersecurity Analyst · SOC · Sentinel/KQL specialist