漏洞介绍
Nginx 在配置 alias
的时候,如果 location
最后不是以斜杠结尾,而 alias
以斜杠结尾,那么将出现目录穿越漏洞。
通过实验了解漏洞
实验靶场用的是《从 0 到 1:CTFer 成长之路》第一章第三节任意文件读取漏洞的第 2 个实验靶场。
靶场的配置文件 docker-compose.yml
内容如下
version: '3.2'
services:
web:
image: registry.cn-hangzhou.aliyuncs.com/n1book/web-file-read-2:latest
ports:
- 80:80
使用 docker-compose up -d
命令启动即可。
nginx 的配置文件在/etc/nginx/sites-available 目录下,内容为:
server {
listen 80;
root /var/www/html/;
index index.html;
server_name _;
autoindex on;
location /img {
alias /tmp/;
}
}
可以看到为路径/img
配置了别名/tmp/
。关键点就在这,location
最后没有斜杠,alias
最后有斜杠,所以存在 nginx 目录穿越漏洞(漏洞原理下面会讲)。
访问 http://localhost/img/img.gif
实际返回的是/tmp/img.gif
。
因为启用了 autoindex
,可以比较方便看到路径下有哪些文件。
利用漏洞,访问 http://localhost/img../
,穿越到了/tmp
的上级目录,也就是根目录。
看到根目录下有 flag 文件,成功拿到 flag,靶场完成!
疑问
运维配置少输入一个斜杠就可能导致漏洞,为什么 Nginx 官方不修复这个漏洞?网上查阅这个漏洞的修复方式,均为修改为正确配置,并没有升级至最新版本这类方法。
这个疑问我也在知乎上提了问题,但并没有得到明确的回答。
后来通过对漏洞原理的深入理解,自己解开了这个疑惑。
漏洞原理
理解目录穿越漏洞首先要知道两个前置知识,一个是 Nginx 配置中 alias 、 proxy_pass 、 root 的机制,另一个是 location 的匹配机制。这里就先不讲 proxy_pass 和 root 了。
找了几篇参考的文章:
- Nginx 代理:路径代理配置 root 、 alias 、 proxy_pass 的区别 – 我命倾尘 – 博客园 (cnblogs.com)
- 一文读懂 nginx 的 location 匹配规则 – wangqingyong – 博客园 (cnblogs.com)
- Nginx location 匹配规则 – Ryan.Miao – 博客园 (cnblogs.com)
- Nginx 中 location 与 root,alias,proxy_pass 的使用 | 王硕’s Blog (ws.lu)
alias 机制和 location 匹配
简单来说,alias 机制就是用 alias
替换 location
匹配到的内容。 location 匹配就是从 url 最左边开始找,
举个例子,假设配置文件如下
location /xxx/ {
alias /path/to/file/;
}
那么访问 http://localhost/xxx/index.html
,/xxx/
替换成/path/to/file/
,那么实际返回的资源是/path/to/file/index.html
文件。
location /xxx {
alias /path/to/file;
}
同理,访问 http://localhost/xxx/index.html
,/xxx
替换成/path/to/file
,那么实际返回的资源还是/path/to/file/index.html
文件。
当配置不当时
location /xxx {
alias /path/to/file/;
}
访问 http://localhost/xxx/index.html
,/xxx
替换成/path/to/file/
,替换结果就是/path/to/file//index.html
,注意这里有两个斜杠,程序会自动修正,多个连续斜杠视为一个斜杠,结果是/path/to/file/index.html
,也就和上面一样。
插个题外话,多个连续斜杠的机制应该是通用的,可以在浏览器里试试 http://localhost////index.html
访问的就是首页,或者在 Linux 里用 ls
命令,查看文件目录/home/dir/
和/home///dir/
结果是一样的。
# ls /var/www/html
index.html
# ls /var///www//html////
index.html
然后就回到漏洞的 payload,当访问/xxx../index.html
时,前面的/xxx
命中匹配,替换成/path/to/file/
,结果为/path/to/file/../index.html
,造成了目录穿越。
直接访问上级目录为什么不行?
比如我直接在浏览器地址栏输入 http://localhost/img/../
,按回车,浏览器自动转成了 http://localhost/
。
那我通过 BurpSuite 抓包,强制修改 URL 呢。
返回的依旧是首页的内容。意思是 URL 发送到后端还会自动计算一次上级目录,修正 URL 后再拿去匹配路由规则。再试试多层上级目录:
好的确认了就是这样,后端会自动修正 URL 中的上级目录,所以直接访问 http://localhost/img/../
没用。
换个玩法,进一步验证匹配和替换机制
假如 location 和 alias 都没有斜杠,是不是可以访问另一个目录名相似的目录呢?
将配置文件修改成:
location /img {
alias /tmp;
}
在根目录创建一个叫 tmpaaa
的文件夹,里面创建一个 hello
文件。
# mkdir /tmpaaa
# touch /tmpaaa/hello
# ls /tmpaaa
hello
访问 http://localhost/imgaaa/
,果不其然,看到了 hello 文件。
解答疑问
所以并不是 Nginx 官方不修复这个漏洞,而是漏洞 「合理」 利用了机制实现了目录穿越,这本身就是 nginx 规则的一部分。