利用PHP 7中的OPcache来实现Webshell

www.96kaifa.com | 2016-10-30 |

摘要:在本文中我们将分析通过利用PHP7默认的 OPcache引擎来对漏洞进行利用的技巧。通过这个漏洞利用技巧,我们将能绕过“禁止web目录的文件读写”(http://www.cyberciti.biz/tips/p...

在本文中我们将分析通过利用PHP7默认的 OPcache引擎来对漏洞进行利用的技巧。通过这个漏洞利用技巧,我们将能绕过“禁止web目录的文件读写”(http://www.cyberciti.biz/tips/php-security-best-practices-tutorial.html)的防护,还能在主机中执行任意代码。

OPcache

OPcache是PHP 7.0内建的新型缓存引擎,它会编译php的脚本,然后在内存中生成对应的字节码。

图片1.png

它还支持在文件系统中进行缓存,我们可以在PHP.ini中指定缓存目录

opcache.file_cache=/tmp/opcache

在上面指定的目录中,OPcache会将编译好的PHP脚本和对应的PHP脚本放在同一个目录结构之中。比如说,/var/www/index.php所编译的脚本会被保存为/tmp/opcache/[system_id]/var/www/index.php.bin。

这里的system_id是一个包含了当前PHP版本信息,Zend框架的扩展ID和各种数据类型信息的哈希值。在Ubuntu最新版16.04中,system_id是由当前Zend框架和PHP的版本号所组成的(81d80d78c6ef96b89afaadc7ffc5d7ea),这些哈希值有可能是被用来确保二进制兼容性的,这个目录会在OPcache第一次进行缓存时生成。

我们将会在下面看到的是每一个OPcache文件还会在文件的header域中保存system_id的对应的副本。

关于OPcache文件夹最有意思的点就在于,用户启动该服务后就会拥有OPcache生成的所有文件夹/文件(在/tmp/opcache/目录之下)的写入权限。

下面是OPcache文件夹的权限情况

图片2.png 

你可以看到OPcache生成的文件夹对用户www-data是可写的,这就导致了我们可以通过重写目录中的缓存文件为webshell,然后执行任意代码。

攻击场景

首先,我们必须获得缓存文件夹的地址(/tmp/opcache/[system_id]),以及目标PHP文件的地址(/var/www/…)。

为了简单起见我们假设网站存在一个phpinfo()文件我们可以从这个文件中获取到缓存文件夹和文件源代码的存储地址,还有在计算system_id的时候将会用到的数据(我们已经开发出一款能够通过phpinfo()文件来计算system_id的工具。你可以在这里下载(https://github.com/GoSecure/php7-opcache-override))。

这里还要再提的一点是目标网站不能对文件上传进行限制。

我们假设php.ini中配置的额外数据为:

opcache.validate_timestamp = 0    ; PHP 7's default is 1

opcache.file_cache_only = 1       ; PHP 7's default is 0

opcache.file_cache = /tmp/opcache

接下来,我们来分析一下攻击的过程:

如下图,我们已经在网站中找到了一个任意上传漏洞,并且/var/www/可写,我们的目标就是将后门代码替换到/tmp/opcache/[system_id]/var/www/index.php.bin中。

图片3.png

1、在本地创建一个包含Webshell的PHP文件,将其命名为”index.php”:

<?php

   system($_GET['cmd']);

?>

2、配置PHP.ini文件中的opcache.file_cache选项。

3、使用php -S 127.0.0.1:8080命令在本地启动一个Web服务器,通过使用命令wget 127.0.0.1:8080向服务器请求index.php文件来触发缓存引擎。

4、定位到我们在第一步中设置的缓存文件夹,你就会发现一个名为index.php.bin的文件,这个文件就是经过编译处理后的webshell,如下图。

图片4.png

5、由于本地system_id很可能与目标主机的system_id不同,所以我们必须打开index.php.bin文件,并将我们的system_id修改成目标主机的system_id。正如之前所提到的,system_id可以被猜解,例如暴力破解,或者根据phpinfo()文件中的服务器信息计算出来(https://github.com/GoSecure/php7-opcache-override/blob/master/system_id_scraper.py)。我们可以在文件签名之后替换system_id,如下图。

图片5.png

6、利用任意上传漏洞将文件上传至/tmp/opcache/[system_id]/var/www/index.php.bin

7、刷新网站的index.php,网站将会自动执行我们的webshell。

图片6.png

更深入一点

php.ini中至少有两个配置项可以造成另类的行为

1、禁止file_cache_only

2、允许validate_timestamp