一.漏洞原因
开发者是为了方便使用类似`#!/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的方式传入。
二.漏洞危害
- 远程代码执行:攻击者可以通过发送特制的HTTP请求来执行任意PHP代码,这可能导致服务器被完全控制。
- 数据泄露:利用此漏洞,攻击者可以访问或修改服务器上的敏感信息,包括数据库内容和配置文件。
- 服务中断:通过执行恶意代码,攻击者可以使服务器停止响应,导致拒绝服务(DoS)情况。
三.容器的镜像加载
cgi模式下有如下可控命令行参数可用:
- `-c` 指定php.ini文件的位置
- `-n` 不要加载php.ini文件
- `-d` 指定配置项
- `-b` 启动fastcgi进程
- `-s` 显示文件源码
- `-T` 执行指定次该文件
- `-h`和`-?` 显示帮助
最简单的利用方式,当然就是`-s`,可以直接显示源码:
burp中改造执行结果
用ls命令列一下目录<?php echo shell_exec("ls"); ?>
得到index.php index.html和info.php,使用cat命令查看一下index.php<?php echo shell_exec("cat index.php"); ?>
<?php echo shell_exec("info.php"); ?>
<?php echo shell_exec("index.html"); ?>
查看根目录<?php echo shell_exec("ls . ./. ./. ./"); ?>
修复原理:获取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);
}
- 更新PHP版本:升级到PHP 5.3.12或更高版本可以修复此漏洞。
- 配置修改:限制对php-cgi的访问,确保只有受信任的用户才能执行相关脚本。同时,禁用Web服务器中的CGI支持也是一种有效的缓解措施。
四:对于在镜像移植中丢失的文件,能有合适的方法补充或添加恢复
- 关闭防火墙
- 从源环境复制文件
- 重建丢失的文件