WAF Bypass Techniques for Bug Bounty Hunters
A WAF block is a clue, not a vulnerability and not the end of the test. The interesting question is whether the edge and application interpret the same bytes differently. If the WAF sees harmless text while the backend reconstructs an SQL fragment, path traversal sequence, or HTML event handler, you have a bypass worth documenting.
Do not begin by firing giant payload lists. You will burn the target's rate limit and learn almost nothing.
Establish which layer answered
Send a clean control, a tiny suspicious token, and one malformed variation. Compare status code, body template, headers, cookies, and timing. A branded 403 delivered in 20 milliseconds likely came from an edge product; a framework error after 400 milliseconds probably reached deeper. Sometimes both layers return 403, so response fingerprints matter.
Use a harmless canary such as wafcheck-7f21 before testing an exploit string. Move that canary through the query, path, JSON body, form body, cookie, and a nonstandard header accepted by the endpoint. Many rule sets inspect these locations unevenly.
Hunt interpretation gaps
Encoding works only when another component decodes it. Try one transformation at a time:
- Percent encoding, then selective double encoding
- Mixed case in keywords or header names
- Unicode characters that normalize into ASCII downstream
- JSON escapes such as
\u003c - Duplicate parameters where proxy and framework choose different values
- Semicolon path parameters or extra slashes normalized by a router
Keep a decoding diary. Record what you sent, what the WAF appeared to inspect, and what the application reflected or acted upon. Without that chain, “double encoding bypasses the WAF” is guesswork.
Content-type confusion is especially productive. An endpoint documented for JSON may also parse form data, XML, or multipart fields. The edge could apply SQL injection rules to application/json while the framework accepts the same field in text/plain after lenient parsing. Preserve the endpoint's normal behavior and alter only Content-Type; changing the payload too makes the result muddy.
Grammar beats random obfuscation
For SQL injection, comments, alternate whitespace, numeric expressions, and database-specific operators can preserve meaning while changing the signature. For XSS, test the exact HTML or JavaScript context: an SVG event, quoted attribute break, or template-literal escape needs different grammar. For traversal, ask which hop converts backslashes, repeated separators, and dot segments.
The backend must prove the payload still works. A 200 response after a 403 is weak if the input is now inert. Show a safe effect: a boolean response difference for SQL injection, a DOM marker for XSS, or retrieval of a non-sensitive canary file in an approved environment.
Report the gap cleanly
Give triage the baseline blocked request, bypass request, decoded backend interpretation, and application effect. Name the WAF only when headers or program staff confirm it. The root issue usually remains unsafe application handling; an edge filter is defense in depth.
Avoid production disruption. Skip expensive regex payloads, oversized bodies, and request floods. One repeatable bypass is stronger than 2,000 scanner hits.
PortSwigger's encoding guide explains how layered decoders create obfuscation opportunities. Your best evidence is a byte-by-byte story showing where the edge stopped understanding the request and where the application started.