翻译自 这篇博客
在本文中,我们将寻求策略来检测和分析隐藏在OPcache文件中的恶意软件。如果你没有看过我们之前关于隐藏PHP7 OPcache文件中的 二进制的webshell文章 中,我们建议在继续之前先阅读。
OPcache是PHP7.0新的内置缓存引擎。它编译PHP脚本,并设置在存储器中的存储产生的字节码。
在php.ini中也可以指定的缓存的目标文件夹:
opcache.file_cache=/tmp/opcache
Persisent secondary file-based cache for OPCahe
在上面的文件夹中,OPCache存储php脚本和相应的编译之后的php脚本在同一个文件夹结构目录之下。例如 /var/www/index.php 编译之后会被存储在 /tmp/opcache/[system_id]/var/www/index.php.bin
上面的system_id是有php的版本号,zend的扩展id和各种数据类型的大小,md5之后得到的。在Ubuntu的最新版本(16.04)中,有当前的zend版本和php(7.0.4-7)产生的system_id是81d80d78c6ef96b89afaadc7ffc5d7ea。这个散列是最有可能用于确保安装时的二进制兼容性。该目录由OPcache缓存它的第一个文件时创建。
如我们将在后面看到,每个OPcache文件将有一个system_id被保存在报头字段中。
有趣的是OPcache所有生成的文件夹/文件(/tmp/opcache下的一切)都会有写的权限作为这个用户正在运行的服务。
下面是opchache文件夹中的权限:
#!shell $ ls /tmp/opcache/ drwx------ 4 www-data www-data 4096 Apr 26 09:16 81d80d78c6ef96b89afaadc7ffc5d7ea
正如你可以在上面看到,通过OPcache生成的文件夹是由www-data用户写入并且有写的权限。如果我们在OPcache目录有写权限,我们可以通过重写与编译的webshell缓存的文件执行任意代码。
首先,我们必须获得高速缓存文件夹(/tmp/opcache/[SYSTEM_ID])的位置,并有针对性的PHP文件的位置(/var/WWW/...)
为了简便起见,我们假设该网站留下了的phpinfo()文件,从中我们可以得到的缓存文件夹位置,文件的源代码的位置,和所有必要的字段来计算SYSTEM_ID。(我们已经创建了一个工具,计算从一个网站的phpinfo的系统ID()。你可以找到它在我们的 GitHub库 )。
值得注意的是,目标网站也必须容易文件上传。
假设使用了默认的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用我们自己的含有后门的文件。
1.创建一个名为index.php的本地的包含的webshell恶意PHP文件:
#!php <?php system($_GET['cmd']); ?>
2.在php.ini文件中的设置opcache.file_cache设置为您选择的地方。
3.运行一个web服务,使用PHP -S 127.0.0.1:8080,然后发送了index.php文件的请求,触发缓存引擎。一个简单的wge t127.0.0.1:8080 将会完成这个工作。
4.再到在步骤1中指定的缓存文件夹;你会发现一个名为index.php.bin文件。这是我们的webshell的编译版本。
5.由于本地SYSTEM_ID将有可能和目标的不同,我们要打开index.php.bin,修改system_id。正如前面提到的,system_id可以通过暴力破解或phpinfo()函数的服务器信息进行计算而得到。要更换system_id所在的文件签名的右面:
6.使用文件上传漏洞,我们上传一个文件/tmp/opcache/[system_id]/var/www/index.php.bin。
7.刷新网站的index.php文件将运行我们的webshell。
php.ini中设置至少有两个配置将创建替代行为。
Disabling file_cache_only Enabling validate_timestamp
Bypassing memory cache (file_cache_only = 0)
如果内存缓存文件缓存优先文件缓存,覆盖OPcache文件将不执行
我们的webshell。如果网站服务器重新启动,这种限制可以被绕过。由于内存缓存将被清空,OPcache将使用文件高速缓存,填补了内存高速缓存,从而执行我们的webshell。
从这个行为看来,不需要重新启动来执行webshell仍然是可能的。
在像WordPress的框架,也有一些过时文件仍然公开访问(例如: functions.php )。
由于这些文件已被弃用,他们不会被装载在内存或在文件系统中没有一个缓存版本。我们上传恶意负载(functions.php.bin),并请求相关的网页(/wp-includes/registration-functions.php)后,OPcache将运行我们的二进制的webshell。
Bypassing timestamp validation (validate_timestamps = 1)
如果启用了时间戳验证,OPcache将检查请求的PHP源文件的时间戳,并将其与缓存文件的时间戳报头字段对比。如果它们不匹配,则高速缓存文件被丢弃,并创建一个新的。为了成功地绕过这个限制,攻击者必须知道目标源文件的时间戳。
话虽这么说,在像WordPress的框架,对源文件的时间戳是作为他们在提取zip或tar时的。
这个有趣的是,一些文件2012年以来没有被修改(functions.php和registration.php的文件)。因此,这些跨越多个版本的WordPress时间戳将会相同。知道时间戳,攻击者就可以相应地修改他的有效载荷,并成功地代替高速缓存,与validate_timestamps设置。时间戳位于从文件的开头34字节:
总之,这种新的攻击向量是针对具体的硬化环境的额外开采技术。它不是影响PHP应用的通用漏洞。随着PHP7.0的发行如Ubuntu16.04的到来,此攻击更需要加强审核您的代码避免文件上传漏洞和警惕潜在危险的服务器配置。