Nginx解析漏洞原理分析

0x01 漏洞描述

漏洞介绍:nginx是一款高性能的web服务器,使用非常广泛,其不仅经常被用作反向代理,也可以非常好的支持PHP的运行。Nginx<8.03默认情况下可能导致服务器错误的将任何类型的文件以PHP的方式进行解析,这将导致严重的安全问题,使得恶意的攻击者可能攻陷支持php的nginx服务器。

0x02 概念介绍

0x21 CGI

CGI是规定web server传递过来的数据是何种标准格式,简单说就是一个协议。web server(例如nginx)只是内容的分发者。比如:

1
2
3
4
5
6
7
8
9
1) 如果请求/index.html,那么web server会去文件系统中找到这个文件,发送给浏览器,这里分发的是静态数据
2) 如果请求的是/index.php,根据配置文件,nginx知道这个不是静态文件,需要去找PHP解析器来处理,那么他会把这个请求简单处理后交给PHP解析器
问题的核心在于Nginx需要传哪些数据给PHP解析器呢,例如:
1) url
2) 查询字符串
3) POST数据
4) HTTP header
...

所以,CGI本质上就是规定要传哪些数据、以什么样的格式传递给后方处理这个请求的协议。

0x22 FastCGI

FastCGI顾名思义也是CGI的一个协议,FastCGI只是优化了CGI程序的性能

1
2
3
4
1) 首先,Fastcgi会先启一个master,解析配置文件,初始化执行环境
2) 然后再启动多个worker
3) 当请求过来时,master会传递给一个worker,然后立即可以接受下一个请求。这样就避免了重复的劳动,效率提高了
4) 而且当worker不够用时,master可以根据配置预先启动几个worker等着,同时如果发现空闲worker太多时,也会停掉一些,这样就提高了性能,也节约了资源

0x23 PHP-CGI/PHP-FastCGI

PHP的解释器是PHP-CGI,PHP-CGI只是个CGI程序,他自己本身只能解析请求,返回结果,不会进程管理

0x24 PHP-FPM

PHP-FPM是PHP-CGI进程的管理器,用来管理PHP-CGI进程的,PHP-FPM的管理对象是PHP-CGI

0x03 漏洞分析

nginx默认以cgi的方式支持php的运行,在配置文件中如下配置:

1
2
3
4
5
6
7
8
location ~ .php$
{
root html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
include fastcgi_params;
}

配置参数说明如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1. location对请求进行选择的时候会使用URI环境变量进行选择
1) 其中传递到后端Fastcgi的关键变量SCRIPT_FILENAME由nginx生成的$fastcgi_script_name决定
2) 而通过分析可以看到$fastcgi_script_name是直接由URI环境变量控制的
2. 这里就是产生问题的点。而为了较好的支持PATH_INFO的提取,在PHP的配置选项里存在cgi.fix_pathinfo选项,其目的是为了从SCRIPT_FILENAME里取出真正的脚本名
我们来假设一个攻击场景:
1. 假设存在一个URL: http://localhost/test/test.jpg
2. 我们以如下的方式去访问: http://localhost/test/test.jpg/test.php
3. nginx将会得到一个URI: /test.jpg/test.php
4. 经过location指令,该请求将会交给后端的fastcgi处理,nginx为其设置环境变量SCRIPT_FILENAME,内容为: /scripts/test.jpg/test.php
5. 后端的fastcgi在接受到该选项时,会根据fix_pathinfo配置决定是否对SCRIPT_FILENAME进行额外的处理,一般情况下如果不对fix_pathinfo进行设置将影响使用PATH_INFO进行路由选择的应用,所以该选项一般配置开启。php通过该选项之后将查找其中真正的脚本文件名字,查找的方式也是查看文件是否存在,这个时候将分离出SCRIPT_FILENAME和PATH_INFO分别为
1) SCRIPT_FILENAME: /scripts/test.jpg
2) PATH_INFO: test.php
6. 最后,以/scripts/test.jpg作为此次请求需要执行的脚本,而nginx会使用php解析器来处理这个jpg文件,攻击者就可以实现让nginx以php来解析任何类型的文件了

所以,漏洞的本质实际上就是由于fcgi和web server对script路径级参数的理解不同出现的问题,这是典型的因为跨系统语境不同导致对同一个请求的不同解释导致的漏洞,它的攻击面是带有这种漏洞的nginx

0x04: 修复方案

  1. 修改php.ini配置
1
cgi.fix_pathinfo = 0
  1. nginx配置文件中添加
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    if ( $fastcgi_script_name ~ ..*/.*php )
    {
    return 403;
    }
    /*
    考虑到MVC框架、用户自定义站点中有可能出现xxx/xx.php的情况,这个规则应该更加细粒度一点,例如*.jpg/.*php、*.txt/.*php
    */
    另外,nginx可以在不需要重启的情况,hotreload配置文件
    service nginx reload
    //or
    /etc/init.d/nginx reload

参考文章:

http://www.80sec.com/nginx-securit.html
http://php.net/manual/zh/ini.core.php
http://www.cnblogs.com/LittleHann/p/4561462.html
http://php.net/manual/zh/install.fpm.configuration.php
http://php.net/manual/zh/install.fpm.php
http://segmentfault.com/q/1010000000256516

大爷,赏个铜板呗!