hkt1998

hkt1998

Nginx目录穿越漏洞

2024-02-27

漏洞介绍

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了。

找了几篇参考的文章:

  1. Nginx代理:路径代理配置root、alias、proxy_pass的区别 - 我命倾尘 - 博客园 (cnblogs.com)

  2. 一文读懂nginx的location匹配规则 - wangqingyong - 博客园 (cnblogs.com)

  3. Nginx location匹配规则 - Ryan.Miao - 博客园 (cnblogs.com)

  4. 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/htmlindex.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规则的一部分。