CVE-2013-4547(文件名逻辑漏洞)¶
漏洞描述¶
**CVE-2013-4547漏洞**是由于非法字符空格和截止符导致Nginx在解析URL时的有限状态机混乱,导致攻击者可以通过一个非编码空格绕过后缀名限制
该漏洞利用了Nginx错误的解析了URL地址,导致可以绕过服务端限制,从而解析PHP文件,造成命令执行的危害。
影响版本¶
- Nginx 0.8.41 ~ 1.4.3
- Nginx 1.5.0 ~ 1.5.7
环境配置¶
- Vulhub项目
- Docker-compose启动
漏洞复现¶
Important
该漏洞利用条件有两个:
- Nginx 0.8.41 ~ 1.4.3 / 1.5.0 ~ 1.5.7
- php-fpm.conf中的security.limit_extensions为空,也就是说任意后缀名都可以解析为PHP
漏洞存在验证¶

Nginx版本符合漏洞利用范围,配置项无法验证

上传文件抓包,将phpinfo.php改为phpinfo.php (后面加个空格)
访问以上url,抓包

改16进制为00,截断
最终效果如上

漏洞原理¶
Nginx解析PHP逻辑¶
Nginx自身是不支持解析PHP语言的,只能通过加载PHP模块来解析PHP。原理图可以看下图:

这里有几个定义:
- CGI:CGI是一种协议,它定义了Nginx或者其他Web Server传递过来的数据格式,全称是(Common Gateway Interface,CGI),CGI是一个独立的程序,独立与WebServer之外,任何语言都可以写CGI程序,例如C、Perl、Python等。
- FastCGI:FastCGI是一种协议,它的前身是CGI,可以简单的理解为是优化版的CGI,拥有更够的稳定性和性能。
- PHP-CGI:只是一个PHP的解释器,本身只能解析请求,返回结果,不会做进程管理。
- PHP-FPM:全称FastCGI Process Manager,看名称就可以知道,PHP-FPM是FastCGI进程的管理器,但前面讲到FastCGI是协议并不是程序,所以它管理的是PHP-CGI,形成了一个类似PHP-CGI进程池的概念。
- Wrapper:字母意思是包装的意思,包装的是谁呢?包装的是FastCGI,通过FastCGI接口,Wrapper接收到请求后,会生成一个新的线程调用PHP解释器来处理数据。
Nginx调用PHP的过程是比较复杂的,需要花大量的时间来学习和梳理。通过原理图和刚才的定义,我们对Nginx处理PHP请求有了大致的了解。那么,Nginx是如何知道将什么样的文件当作PHP文件处理?是在nginx.conf配置文件中的
location ~ \.php$ {
root html;
include fastcgi_params;
fastcgi_pass IP:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT /var/www/html;
}
location后面的.php$代表了以.php结尾的文件都安装花括号中的内容执行,其中fastcgi_pass就是nginx和php-fpm的媒介,Nginx将请求通过fastcgi_pass转发给php-fpm。fastcgi_pass可以和Nginx不在同一台服务器上,他们通过IP+PORT的方式进行通信。
security.limit_extensions配置项¶
限制 FPM 允许解析的脚本扩展名。 此设置可以预防 web 服务器配置的错误。 应当限制 FPM 仅仅解析 .php 扩展名,阻止恶意用户使用其他扩展名运行 php 代码。 默认值: .php .phar
参考PHP官方文档:https://www.php.net/manual/zh/install.fpm.configuration.php
漏洞触发分析¶
根据nginx.conf文件中location中的定义,以.php结尾的文件都解析为php。若我们访问的文件名为shell.gif[0x20][0x00].php,该文件名以.php结尾可以被FastCGI接收,FastCGI在读取文件名时被00截断,导致读取的文件名为1.gif[0x20],配合limit_extensions为空即可利用成功。该漏洞利用条件有两个:
- Nginx 0.8.41 ~ 1.4.3 / 1.5.0 ~ 1.5.7
php-fpm.conf中的security.limit_extensions为空,也就是说任意后缀名都可以解析为PHP

可以看到Vulhub中设置成为空值
关于空格的必要性分析¶
nginx发现文件名中存在\0截断符时会返回错误信息代码如下所示,这也就是为什么我们上面没有接收到任何的响应信息原因:
在仔细调试代码发现但如果nginx发现URI中存在空格会跳到如下代码,这时ch=\0
上面代码执行完后state='sw_check_uri’然后继续处理URI中的剩余部分,这说明了如果空格和零截断符相邻的话nginx就不会检测到
零截断并放回错误了,这时一个逻辑漏洞
参考文章:https://blog.csdn.net/Blood_Pupil/article/details/88565176
修复建议¶
security.limit_extensions不要设置为空(默认为php)- 升级Nginx版本