首页 » 网站攻防 » 浏览内容

利用PHP7中的OPcache来实现Webshell

2876 0 发表评论

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

OPcache

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

14647069721303

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

在上面指定的目录中,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文件夹的权限情况

14647069797259

你可以看到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中配置的额外数据为:

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

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

14647069933096

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

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,如下图。

14647070012099

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,如下图。

14647070146080

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

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

1464707021699

更深入一点

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

1、禁止file_cache_only

2、允许validate_timestamp

绕过内存缓存(file_cache_only = 0)

如果内存缓存的优先级高于文件缓存,那么重写OPcache文件并不会执行我们的webshell。在上传完webshell后,如果服务器重启之后,我们就可以绕过这种限制。由于内存缓存将被清空,OPcache这时会将文件缓存填充到内存中,从而执行了我们的webshell。

因为上面说到的这种机制的原因,我们将很有可能在服务器不重启的情况下实现webshell的执行。

在WordPress等网站框架之中,还是会有一些过时的文件可以被公开访问到(比如registration-functions.php(https://github.com/WordPress/WordPress/blob/703d5bdc8deb17781e9c6d8f0dd7e2c6b6353885/wp-includes/registration-functions.php))。

由于这些文件已经过时了,所以系统不会再加载这些文件,也不会在内存和文件系统的缓存中有这些缓存的文件。在我们上传了恶意代码(registration-functions.php.bin)之后访问相关的网页(/wp-includes/registration-functions.php),OPcache就会自动执行我们的webshell。

绕过时间戳认证(validate_timestamps = 1)

如果服务器启用了时间戳认证功能,OPcache将会对被请求的PHP源文件的时间戳进行验证。如果该文件的时间戳与缓存文件header域中的时间戳相匹配,那么服务器就会允许访问。反之,缓存文件将会被丢弃,并创建出一个新的缓存文件。为了绕过这种限制,攻击者必须知道目标源文件的时间戳。

这也就意味着,在WordPress等网站框架之中,源文件的时间戳是可以获取到的,因为当开发人员将代码文件从压缩包中解压出来之后,时间戳信息仍然是保持不变的,如下图。

14647070304948

有趣的是,其中的有些文件从2012年起就再也没有进行过任何的修改(请注意registration-functions.php和registration.php文件)。因此,即使是不同版本的WordPress,它们的时间戳也是一样的。在获取到了文件时间戳的信息之后,攻击者就可以修改他们的恶意代码,并且成功覆盖服务器的缓存数据。时间戳位于文件开头的第34字节的位置,如图

14647070387201
Demo

正如我们在此之前提到的,大家可以在我们的GitHub代码库(https://github.com/GoSecure/php7-opcache-override)中下载工具。工具中包含了 010编辑模板、生成SYSTEM_ID的工具还有本篇文章所用到的网页代码。

结论

总之,这种新型的攻击方法针对具体的环境而言的,因为这并不是PHP的通用漏洞。很多操作系统,例如Ubuntu 16.04都会默认安装PHP 7,所以在我们的开发过程中,更加应该谨慎地审查我们的代码,并检查网站中是否存在文件上传漏洞。

标签:

评论 共 0 条 (RSS 2.0) 发表 评论

  1. 暂无评论,快来抢沙发!

发表评论

  •   没有头像?

关注我们,实时联系

通知公告">更多...

♥如果喜欢本站可以直接点击订阅博客
♥点击此加入晓残博客会员群加入扣群
♥本站免费提供加密解密服务点击解密
♥伙伴们喜欢本站赏个好评呗

关注微信

有偿服务

博客统计

  • 日志总数:478 篇
  • 评论数目:1481 条
  • 标签总数:40 个
  • 页面总数:8 个
  • 分类总数:17 个
  • 友链总数:25 个
  • 建站日期:2011-02-13
  • 运行天数:2127 天
  • 最后更新:2016-12-9

订阅博客