# 前言
继续填坑做复现,不知道犯懒和强迫症是怎么同时出现在我身上的,每次都是因为强迫症万分痛苦的填坑。前面复现了常见的 IIS 和 Apache 的漏洞,这篇 blog 简单的记录一下 Nginx 相关的漏洞。
# Nginx
还是很有必要先介绍一下 Nginx,Nginx 是一个高性能的开源 Web 服务器软件,也可以用作反向代理服务器、负载均衡和 Http 缓存。因为 Nginx 采用时间驱动和异步非阻塞的处理模型,它能处理大量并发连接和高负载。在 Netcraft 最新的服务器调查 blog 里,其市场占比非常可观:2024 年 2 月 Web 服务器调查 | 网艺 (netcraft.com)
# 漏洞复现
# Nginx 配置错误导致漏洞
这类漏洞与 Nginx 版本无关,只与 Nginx 的配置有关
# CRLF 注入漏洞
# 漏洞成因
CRLF 是 "回车 + 换行" 的简称,在 http 协议中 HTTP header 域 HTTP body 是用两个 CRLF 分隔的。浏览器会通过这两个 CRFL 来区别 header 和 body,首先来看下面两个场景:
- 当访问
http://blog.xcu.icu/
会自动跳转到https://blog.xcu.icu/
- 当访问
http://example.com
会自动跳转到http://www.example.com
当跳转的时候我们需要保证用户访问的页面不会发生变化,所以就需要从 Nginx 中获取用户请求的文件路径,参考文档模块 ngx_http_rewrite_module (nginx.org) 对应的中文文档 HttpRewrite 模块 | Nginx 中文官方文档 (gitbooks.io)
Nginx 提供了三个变量
$uri
变量包含请求中的 URI,不包括参数(就是?后面的部分)$document_uri
变量包含请求的 URI,与上面的$uri
不同的是它包含所有的查询参数$requesr_uri
变量包含的是客户端发送的原始请求 URI,包括所有的查询参数(这里的原始是指解码之前的)
所以如果重定向使用的是前两个比如
location / { | |
return 302 https://$host$uri; | |
} |
这种写法会导致请求中的换行符也被包含在内,导致修改返回包中的数据
# 环境搭建
修改 /etc/nginx/conf.d/default.conf
配置文件
server { | |
listen 80; | |
server_name localhost; | |
location / { | |
root /usr/share/nginx/html; | |
index index.html index.html; | |
return 302 http://$host:$server_port$uri; | |
} | |
error_page 500 502 503 504 /50x.html; | |
location = /50x.html { | |
root /usr/share/nginx/html; | |
} | |
} |
这里我使用 docker 搭建环境,Dockerfile 如下
FROM nginx:latest | |
COPY files /tmp/files/ | |
COPY conf /tmp/conf/ | |
RUN mv /tmp/files/start.sh / && \ | |
chmod +x /start.sh && \ | |
cp /tmp/conf/change.conf /etc/nginx/conf.d/default.conf && \ | |
rm -rf /tmp/files && \ | |
rm -rf /tmp/conf | |
CMD /start.sh | |
EXPOSE 80 |
# 漏洞复现
curl -I http://192.168.246.136/%0d%0aSet-test:%20aaabbb |
或者多加一个 CRLF 导致反射型 XSS
%0D%0A%0D%0A%3Cimg%20src=1%20test=alert(/xss/)%3E
# 修复建议
在获取用户的请求路径时,配置文件内出现的配置应当是 $request_uri
如下
location / { | |
return 302 https://$host$request_uri; | |
} |
# 目录穿越漏洞
# 漏洞成因
同样是一个配置导致的漏洞,如果当管理员在映射本地文件系统中的某个目录的时候少写了一个 / 如下
location /files { | |
alias /home/; | |
} |
这里就是将本地 test 文件映射到 files 上,在 files 后面没有 / 就会导致目录穿越,
# 环境搭建
修改配置文件如下
server { | |
listen 80; | |
server_name localhost; | |
location / { | |
root /usr/share/nginx/html; | |
index index.html index.html; | |
} | |
location /files { | |
alias /home/; | |
} | |
error_page 500 502 503 504 /50x.html; | |
location = /50x.html { | |
root /usr/share/nginx/html; | |
} | |
} |
dockerfile 文件如下
FROM nginx:latest | |
COPY files /tmp/files/ | |
COPY conf /tmp/conf/ | |
RUN mv /tmp/files/flag.sh / && \ | |
mv /tmp/files/start.sh / && \ | |
chmod +x /flag.sh /start.sh && \ | |
cp /tmp/conf/change.conf /etc/nginx/conf.d/default.conf && \ | |
rm -rf /tmp/files && \ | |
rm -rf /tmp/conf | |
CMD /start.sh | |
EXPOSE 80 | |
ENV FLAG flag{test123} |
这里我加上了 flag 字段,读取这个文件即可
# 漏洞复现
payload: /files../flag
# 漏洞建议
修复就针对配置文件
location /files/ { | |
alias /home/; | |
} |
这里将 files 后的 / 加上即可
# Nginx 解析漏洞
# 漏洞成因
当服务器配置有下面的内容时
location ~ \.php$ {} |
这个配置用于处理 php 后缀结尾的请求,当收到 php 结尾的请求后会将请求传递给 php 解释器处理。而 php 解析器默认有配置
cgi.fix_pathinfo = 1 |
开启的情况下会对文件路经做一个修复。比如是访问一个 /aaa.png/bbb
如果这里没有找到 bbb 这个文件会删除 bbb 这个路径,如果解析规则没做限制会尝试解析 aaaa.png
# 环境搭建
nginx 对应的配置文件
server { | |
listen 80; | |
server_name localhost; | |
location / { | |
root /usr/share/nginx/html; | |
index index.html index.php; | |
} | |
error_page 500 502 503 504 /50x.html; | |
location = /50x.html { | |
root /usr/share/nginx/html; | |
} | |
location ~ \.php$ { | |
fastcgi_index index.php; | |
include fastcgi_params; | |
fastcgi_param REDIRECT_STATUS 200; | |
fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name; | |
fastcgi_param DOCUMENT_ROOT /var/www/html; | |
fastcgi_pass php:9000; | |
} | |
} |
php 对应的配置文件
[www] | |
security.limit_extensions = |
上传文件的代码这里不再展示
# 漏洞复现
尝试上传一个 info.png 文件
然后去尝试访问这个 png 文件
后面加上.php
# 修复建议
- 将 php.ini 文件中的 cgi.fix_pathinfo 的值设置为 0
- php-fpm.conf 中的 security.limit_extensions 后面的值设置为.php
# Nginx 程序漏洞
下面的漏洞就已经是 Nginx 本身的问题了,我们能做的修复就是更新版本
# CVE-2013-4547
# 影响版本
Nginx 0.8.41 ~ 1.4.3 / 1.5.0 ~ 1.5.7
# 漏洞成因
首先要大概了解 Nginx 中 php 是如何被解析的
还是这张图,当浏览器发出静态请求在 nginx 上直接就处理后返回给浏览器,但是当我们尝试访问一个动态资源,Nginx 就会根据请求的文件去选择解释器,像上面的请求就会去尝试使用 php 解析器。Nginx 是如何知道将什么样的文件当作 PHP 文件处理?是在 nginx.conf 配置文件中的
location ~ \.php$ { | |
root html; | |
include fastcgi_params; | |
fastcgi_pass php:9000; | |
fastcgi_index index.php; | |
fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name; | |
fastcgi_param DOCUMENT_ROOT /var/www/html; | |
} |
\.php$
代表了以 .php
结尾的文件都按照花括号中的内容执行。问题同样也出现在正则这里,当我们尝试请求 info.png[0x20][0x00].php
时会匹配到这个正则交由 php 解析器处理,但是因为 0x00
是截断所以 Nginx 会认为请求的文件是 info.png[0x20]
,如果这里 php 服务器上没有指定 php 扩展名就会导致 info.png[0x20]
被解析
# 环境搭建
# Nginx 部分
配置文件如下
worker_processes 1; | |
events { | |
worker_connections 1024; | |
} | |
http { | |
include mime.types; | |
default_type application/octet-stream; | |
sendfile on; | |
keepalive_timeout 65; | |
server { | |
listen 80; | |
server_name localhost; | |
root html; | |
index index.php; | |
charset utf-8; | |
location ~ \.php$ { | |
root html; | |
include fastcgi_params; | |
fastcgi_pass php:9000; | |
fastcgi_index index.php; | |
fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name; | |
fastcgi_param DOCUMENT_ROOT /var/www/html; | |
} | |
} | |
} |
# php 部分
[www] | |
security.limit_extensions = | |
php_admin_flag[cgi.fix_pathinfo] = off |
# 漏洞复现
尝试上传一个文件 info.png[0x20]
然后访问如下