PHP_MOD以模块化的方式运行:将PHP集成到Apache中,以同一个进程运行
CGI(公共网关接口)Common Gateway InterfaceApache在遇到PHP脚本时,会将其提交给CGI(php-cgi)应用程序进行处理,解释之后再将结果返回给Apache,然后再返回给请求的用户(CGI是规定web server传递过来的数据是何种标准格式,简单说就是一个协议)
如果请求/index.html,那么web server会去文件系统中找到这个文件,发送给浏览器,这里分发的是静态数据如果请求的是/index.php,根据配置文件,nginx知道这个不是静态文件,需要去找PHP解析器来处理,那么他会把这个请求简单处理后交给PHP解析器需要时才创建
FASTCGI (多进程的CGI)这种形式是CGI的加强版本,CGI是单进程、多线程的运行方式,程序执行完成之后就会销毁,所以每次都需要加载配置和环境变量(创建-执行)
而FastCGI则不同,FastCGI像是一个常驻 (long-live) 型的CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去创建新的进程 。FastCGI进程管理器自身初始化,启动多个CGI解释器进程 (在任务管理器中可见多个php-cgi)并等待来自web server的连接
首先,Fastcgi会先启一个master,解析配置文件,初始化执行环境然后再启动多个worker当请求过来时,master会传递给一个worker,然后立即可以接受下一个请求。这样就避免了重复的劳动,效率提高了而且当worker不够用时,master可以根据配置预先启动几个worker等着,同时如果发现空闲worker太多时,也会停掉一些,这样就提高了性能,也节约了资源提前创建好
Nginx解析漏洞:nginx默认以cgi的方式支持php的运行,在配置文件中如下配置:
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;
}
背景说明PHP中,经常要获取当前请求的URL的路径信息,一般可以通过环境变量$_SERVER['PATH_INFO']获取,而cgi.fix_pathinfo选项与这个值得获取有关,PATH_INFO是一个CGI1.1的标准,经常用来作为传递参数给后端的CGI Server
被很多系统用来优化url路径格式,比如对于很多框架,下面这个网址
比如:http://shtwo.top/tag/Rev27?c=index&m=search中
$_SERVER['PATH_INFO'] = '/tag/Rev27'$_SERVER['QUERY_STRING'] = 'c=index&m=search'php.ini中的配置参数cgi.fix_pathinfo=1,它是用来对设置cgi模式下为php是否提供绝对路径信息或PATH_INFO信息
【若PHP获取不到PATH_INFO信息,那些依赖PATH_INFO进行URL美化的程序就失效了】
配置参数location对请求进行选择的时候会使用URI环境变量(也就是URL中的传参以及目录格式)进行选择其中传递到后端Fastcgi的关键变量SCRIPT_FILENAME由nginx生成的$fastcgi_script_name决定
【fastcgi_param指令负责将参数传给FastCGI Server】
而通过分析可以看到 $fastcgi_script_name是直接由URI环境变量控制的
这里就是产生问题的点:而为了较好的支持PATH_INFO的提取,在PHP的配置选项里存在cgi.fix_pathinfo选项,其目的是为了从SCRIPT_FILENAME里取出最终要执行的那个文件的名称攻击场景假设存在一个URL:http://shtwo.top
我们以如下的方式去访问: http://shtwo.top/test/test.jpg/test.php漏洞利用
nginx将会得到:/test.jpg/test.php参数
经过location指令,该请求将会交给后端的fastcgi处理,nginx为其设置环境变量SCRIPT_FILENAME,内容为 :/scripts/test.jpg/test.php
后端的fastcgi在接受到该选项时,会根据fix_pathinfo配置决定是否对SCRIPT_FILENAME进行额外的处理 (cgi_fix_pathinfo = 1),一般情况下如果不对fix_pathinfo进行设置将影响使用PATH_INFO进行路由选择的应用,所以该选项一般配置开启
【cgi.fix_pathinfo=1用于修复路径,如果当前路径不存在则采用上层路径】
php通过该选项查找的方式是查看文件是否存在,在检查到test.php不存在后,将采用上层路径,也就是test.jpg
此时分离出来的:
PATH_INFO:test.phpSCRIPT_INFO:/script/test.jpg//判断逻辑
if (script_path_translated &&
(script_path_translated_len = strlen(script_path_translated)) > 0 &&
(script_path_translated[script_path_translated_len-1] == '/' ||
最后,由于test.php不存在,以/scripts/test.jpg作为此次请求需要执行的脚本,而nginx会使用php解析器来处理这个jpg文件,攻击者就可以实现让nginx以php来解析任何类型的文件
漏洞的本质实际上就是由于fastcgi和web server对script路径级参数的理解不同出现的问题:
Nginx接收到/test.jpg/test.php的请求时,其 PATH_INFO会设置为/test.jpg/test.php,由于其中请求了php文件,需要解析,于是直接将该路径 PATH_INFO传递给CGI,拼接给其$fastcgi_script_name,交给CGI进行解析,CGI收到后会尝试待解析的文件是否存在,若开启了cgi_fix_pathinfo选项,则当前路径的文件不存在,会继续尝试上一级文件,而此时若是没有设置security.limit_extensions=.php限制只能解析.php的文件,则会将所有后缀的文件都尝试以php文件进行解析,这也就带来了安全的风险
这是典型的因为跨系统语境不同导致对同一个请求的不同解释导致的漏洞,它的攻击面是带有这种漏洞的nginx
预防方式cgi_fix_pathinfo=0不合实际,会影响实际业务将php-fpm.conf中的 security.limit_extensions=.php(该配置将限制fastcgi解析文件的格式),此项设置为空的时候才允许fastcgi将.png等文件当做代码解析深入学习Nginx 与 pathinfo - Steemit