Nginx

从零开始学习AWS黑客技术,成为专家 htARTE(HackTricks AWS Red Team Expert)

支持HackTricks的其他方式:

即时提供的漏洞评估和渗透测试设置。从任何地方运行完整的渗透测试,具有从侦察到报告的20多种工具和功能。我们不取代渗透测试人员 - 我们开发定制工具、检测和利用模块,为他们节省一些时间,深入挖掘,弹出shell,并享受乐趣。

缺少根位置

在配置Nginx服务器时,root指令通过定义文件提供服务的基本目录起着关键作用。请考虑下面的示例:

server {
root /etc/nginx;

location /hello.txt {
try_files $uri $uri/ =404;
proxy_pass http://127.0.0.1:8080/;
}
}

在这个配置中,/etc/nginx 被指定为根目录。这种设置允许访问指定根目录内的文件,比如 /hello.txt。然而,需要注意的是只定义了一个特定位置 (/hello.txt),并没有配置根位置 (location / {...})。这个遗漏意味着根指令适用于全局,使得对根路径 / 的请求可以访问 /etc/nginx 下的文件。

这个配置带来了一个关键的安全考虑。一个简单的 GET 请求,比如 GET /nginx.conf,可能通过提供位于 /etc/nginx/nginx.conf 的 Nginx 配置文件来暴露敏感信息。将根目录设置为一个不太敏感的目录,比如 /etc,可以减轻这个风险,但仍可能允许意外访问其他关键文件,包括其他配置文件、访问日志,甚至用于 HTTP 基本身份验证的加密凭据。

别名 LFI 配置错误

在 Nginx 的配置文件中,需要仔细检查 "location" 指令。一个称为本地文件包含 (LFI) 的漏洞可能会通过类似以下配置而被无意中引入:

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

这种配置容易受到LFI攻击的影响,因为服务器会将诸如/imgs../flag.txt这样的请求解释为试图访问目标目录之外的文件,最终解析为/path/images/../flag.txt。这个漏洞允许攻击者从服务器的文件系统中检索文件,这些文件本不应通过网络访问。

为了减轻这种漏洞,应调整配置为:

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;
}
pageProxy / WAF Protections Bypass

不安全的变量使用 / HTTP请求拆分

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

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

location ~ /docs/([^/])? { … $1 … } - 易受攻击

location ~ /docs/([^/\s])? { … $1 … } - 不易受攻击(检查空格)

location ~ /docs/(.*)? { … $1 … } - 不易受攻击

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

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

字符 \r (回车) 和 \n (换行) 表示 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注入和响应拆分的风险,请访问https://blog.detectify.com/2019/06/14/http-response-splitting-exploitations-and-mitigations/.

此技术也在这个讲座中得到解释,其中提供了一些易受攻击的示例和检测机制。例如,为了从黑盒角度检测此错误配置,您可以使用以下请求:

  • https://example.com/%20X - 任何HTTP代码

  • https://example.com/%20H - 400 Bad Request

如果存在漏洞,第一个请求将返回为"X",因为"X"是任何HTTP方法,而第二个请求将返回错误,因为"H"不是有效的方法。因此,服务器将接收类似于GET / H HTTP/1.1的内容,从而触发错误。

其他检测示例包括:

  • http://company.tld/%20HTTP/1.1%0D%0AXXXX:%20x - 任何HTTP代码

  • 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;
}
  • 请注意再次在 URL 中出现的 $uri(这次是作为参数的一部分)

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的一份安全报告中得到了突出,可以在这里查看。对错误消息的进一步调查导致在Nginx代码库的SSI过滤模块中确定了其发生,将服务器端包含(SSI)指定为根本原因。

为了检测这种错误配置,可以执行以下命令,其中涉及设置一个referer头来测试变量打印:

$ curl -H ‘Referer: bar’ http://localhost/foo$http_referer | grep ‘foobar’

系统中对此错误配置的扫描显示,有多个实例允许用户打印Nginx变量。然而,脆弱实例数量的减少表明修补此问题的努力在某种程度上取得了成功。

后端原始响应读取

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应用程序,Nginx会拦截并处理500错误响应。

  • proxy_hide_header: 正如其名称所示,此指令会隐藏指定的HTTP标头,增强隐私和安全性。

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

merge_slashes设置为关闭

默认情况下,Nginx的**merge_slashes指令设置为on,它会将URL中的多个斜杠压缩为单个斜杠。这个功能虽然简化了URL处理,但可能会意外地隐藏在Nginx后面的应用程序中的漏洞,特别是那些容易受到本地文件包含(LFI)攻击的应用程序。安全专家Danny Robinson和Rotem Bar**已经强调了与这种默认行为相关的潜在风险,特别是当Nginx充当反向代理时。

为了减轻这种风险,建议关闭merge_slashes指令,以保护容易受到这些漏洞影响的应用程序。这样可以确保Nginx将请求转发给应用程序而不会改变URL结构,从而不会掩盖任何潜在的安全问题。

有关更多信息,请查看Danny Robinson和Rotem Bar

恶意响应标头

此篇文章所示,如果Web服务器的响应中存在某些标头,它们将改变Nginx代理的行为。您可以在文档中查看它们:

  • 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指令通常在授权控制中发挥作用。一个常见的错误是没有指定默认值,这可能导致未经授权的访问。例如:

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";
}
}

/map-poc 内访问未定义的 URI时,恶意用户可以绕过安全性。Nginx 手册建议设置默认值以避免此类问题。

DNS欺骗漏洞

在某些条件下,对 Nginx 进行 DNS 欺骗是可行的。如果攻击者知道 Nginx 使用的DNS服务器并能拦截其 DNS 查询,则可以欺骗 DNS 记录。然而,如果 Nginx 配置为使用本地主机 (127.0.0.1) 进行 DNS 解析,则此方法无效。Nginx 允许如下指定 DNS 服务器:

resolver 8.8.8.8;

proxy_passinternal指令

**proxy_pass指令用于将请求重定向到其他服务器,无论是在内部还是在外部。internal**指令确保某些位置仅在Nginx内部可访问。虽然这些指令本身并非漏洞,但它们的配置需要仔细检查,以防止安全漏洞。

proxy_set_header Upgrade & Connection

如果nginx服务器配置为传递Upgrade和Connection标头,则可能会执行h2c Smuggling攻击以访问受保护/内部端点。

此漏洞将允许攻击者与proxy_pass端点(在本例中为http://backend:9999)建立直接连接,nginx不会检查其内容。

以下是一个易受攻击配置的示例,可从此处窃取/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;
}

请注意,即使 proxy_pass 指向特定的 路径,比如 http://backend:9999/socket.io,连接也将建立在 http://backend:9999,因此您可以访问内部端点中的任何其他路径。因此,在 proxy_pass 的 URL 中指定路径并不重要。

亲自尝试

Detectify 创建了一个 GitHub 存储库,您可以使用 Docker 设置自己的易受攻击的 Nginx 测试服务器,其中包含本文讨论的一些错误配置,并尝试自行查找它们!

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

静态分析工具

Gixy 是一个用于分析 Nginx 配置的工具。Gixy 的主要目标是防止安全配置错误并自动化缺陷检测。

Nginxpwner 是一个简单的工具,用于查找常见的 Nginx 配置错误和漏洞。

参考资料

即时可用的漏洞评估和渗透测试设置。从侦察到报告,使用 20 多种工具和功能运行完整的渗透测试。我们不取代渗透测试人员 - 我们开发定制工具、检测和利用模块,让他们有更多时间深入挖掘、弹出 shell 并享受乐趣。

从零开始学习 AWS 黑客技术,成为专家 htARTE (HackTricks AWS Red Team Expert)

支持 HackTricks 的其他方式:

最后更新于