利用 PHP7 的 OPcache 执行 PHP 代码
from:http://blog.gosecure.ca/2016/04/27/binary-webshell-through-opcache-in-php-7/
在 PHP 7.0 发布之初,就有不少 PHP 开发人员对其性能提升方面非常关注。在引入 OPcache 后,PHP的性能的确有了很大的提升,之后,很多开发人员都开始采用 OPcache 作为 PHP 应用的加速器。OPcache 带来良好性能的同时也带来了新的安全隐患,下面的内容是 GoSecure 博客发表的一篇针对 PHP 7.0 的 OPcache 执行 PHP 代码的技术博文。
本文会介绍一种新的在 PHP7 中利用默认的 OPcache 引擎实施攻击的方式。利用此攻击向量,攻击者可以绕过“Web 目录禁止文件读写”的限制 ,也可以执行他自己的恶意代码。
0x00 OPcache 利用方式简介
OPcache 是 PHP 7.0 中内建的缓存引擎。它通过编译 PHP 脚本文件为字节码,并将字节码放到内存中。
使用 PHP7 加速 Web 应用
OPcache 缓存文件格式请看 这里。
同时,它在文件系统中也提供了缓存文件。在 PHP.ini 中配置如下,你需要指定一个缓存目录:
opcache.file_cache=/tmp/opcache
在指定的目录中,OPcache 存储了已编译的 PHP 脚本文件,这些缓存文件被放置在和 Web 目录一致的目录结构中。如,编译后的 /var/www/index.php 文件的缓存会被存储在 /tmp/opcache/[system_id]/var/www/index.php.bin 中。
system_id是当前 PHP 版本号,Zend 扩展版本号以及各个数据类型大小的 MD5 哈希值。在最新版的 Ubuntu(16.04)中, system_id是通过当前 Zend 和 PHP 的版本号计算出来的,其值为 81d80d78c6ef96b89afaadc7ffc5d7ea。这个哈希值很有可能被用来确保多个安装版本中二进制缓存文件的兼容性。当 OPcache 在第一次缓存文件时,上述目录就会被创建。
在本文的后面,我们会看到每一个 OPcache 缓存文件的文件头里面都存储了 system_id。
有意思的是,运行 Web 服务的用户对 OPcache 缓存目录(如:/tmp/opcache/)里面的所有子目录以及文件都具有写权限。
#!shell$ ls /tmp/opcache/drwx------ 4 www-data www-data 4096 Apr 26 09:16 81d80d78c6ef96b89afaadc7ffc5d7ea
正如你所看到的,www-data 用户对 OPcache 缓存目录有写权限,因此,我们可以通过使用一个已经编译过的 webshell 的缓存文件替换 OPcache 缓存目录中已有的缓存文件来达到执行恶意代码的目的。
0x01 OPcache 利用场景
要利用 OPcache 执行代码,我们需要先找到 OPcache 的缓存目录(如:/tmp/opcache/[ system_id])以及 Web 目录(如:/var/www/)。
假设,目标站点已经存在一个执行 phpinfo() 函数的文件了。通过这个文件,我们可以获得 OPcache 缓存目录, Web 目录,以及计算 system_id所需的几个字段值。 我写了一个脚本,可以利用 phpinfo() 计算出 system_id。
另外还要注意,目标站点必须存在一个文件上传漏洞。假设 php.ini 配置 opcache 的选项如下:
opcache.validate_timestamp = 0 ; PHP 7 的默认值为 1opcache.file_cache_only = 1 ; PHP 7 的默认值为 0opcache.file_cache = /tmp/opcache
此时,我们可以利用上传漏洞将文件上传到 Web 目录,但是发现 Web 目录没有读写权限。这个时候,就可以通过替换 /tmp/opcache/[system_id]/var/www/index.php.bin 为一个 webshell的二进制缓存文件运行 webshell。
-
在本地创建 webshell 文件 index.php ,代码如下:
#!php<?php system($_GET['cmd']); ?>
登录后复制 -
在 PHP.ini 文件中设置 opcache.file_cache 为你所想要指定的缓存目录
-
运行 PHP 服务器(php -S 127.0.0.1:8080) ,然后向 index.php 发送请求(wget 127.0.0.1:8080),触发缓存引擎进行文件缓存。
-
打开你所设置的缓存目录,index.php.bin 文件即为编译后的 webshell 二进制缓存文件。
-
修改 index.php.bin 文件头里的 system_id为目标站点的 system_id。在文件头里的签名部分的后面就是 system_id的值。
-
通过上传漏洞将修改后的 index.php.bin 上传至 /tmp/opcache/[system_id]/var/www/index.php.bin ,覆盖掉原来的 index.php.bin
-
重新访问 index.php ,此时就运行了我们的 webshell
- 禁用 file_cache_only
- 启用 validate_timestamp
针对这种攻击方式,在 php.ini 至少有两种配置方式可以防御此类攻击。
0x02 绕过内存缓存(file_cache_only = 0)
如果内存缓存方式的优先级高于文件缓存,那么重写后的 OPcache 文件(webshell)是不会被执行的。但是,当 Web 服务器重启后,就可以绕过此限制。因为,当服务器重启之后,内存中的缓存为空,此时,OPcache 会使用文件缓存的数据填充内存缓存的数据,这样,webshell 就可以被执行了。
但是这个方法比较鸡肋,需要服务器重启。那有没有办法不需要服务器重启就能执行 webshell 呢?
后来,我发现在诸如 WordPress 等这类框架里面,有许多过时不用的文件依旧在发布的版本中能够访问。如: registration-functions.php
由于这些文件过时了,所以这些文件在 Web 服务器运行时是不会被加载的,这也就意味着这些文件没有任何文件或内存的缓存内容。这种情况下,通过上传 webshell 的二进制缓存文件为 registration-functions.php.bin ,之后请求访问 /wp-includes/registration-functions.php ,此时 OPcache 就会加载我们所上传的 registration-functions.php.bin 缓存文件。
0x03 绕过时间戳校验(validate_timestamps = 1)
如果服务器启用了时间戳校验,OPcache 会将被请求访问的 php 源文件的时间戳与对应的缓存文件的时间戳进行对比校验。如果两个时间戳不匹配,缓存文件将被丢弃,并且重新生成一份新的缓存文件。要想绕过此限制,攻击者必须知道目标源文件的时间戳。如上面所说的,在 WordPress 这类框架里面,很多源文件的时间戳在解压 zip 或 tar 包的时候都是不会变的。
注意观察上图,你会发现有些文件从2012年之后从没有被修改过,如:registration-functions.php 和 registration.php 。因此,这些文件在 WordPress 的多个版本中都是一样的。知道了时间戳,攻击者就可以绕过 validate_timestamps 限制,成功覆盖缓存文件,执行 webshell。二进制缓存文件的时间戳在 34字节偏移处。
0x04 总结
OPcache 这种新的攻击向量提供了一些绕过限制的攻击方式。但是它并非一种通用的 PHP 漏洞。随着 PHP 7.0 的普及率不断提升,你将很有必要审计你的代码,避免出现上传漏洞。并且检查可能出现的危险配置项。

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

JWT是一种基于JSON的开放标准,用于在各方之间安全地传输信息,主要用于身份验证和信息交换。1.JWT由Header、Payload和Signature三部分组成。2.JWT的工作原理包括生成JWT、验证JWT和解析Payload三个步骤。3.在PHP中使用JWT进行身份验证时,可以生成和验证JWT,并在高级用法中包含用户角色和权限信息。4.常见错误包括签名验证失败、令牌过期和Payload过大,调试技巧包括使用调试工具和日志记录。5.性能优化和最佳实践包括使用合适的签名算法、合理设置有效期、

会话劫持可以通过以下步骤实现:1.获取会话ID,2.使用会话ID,3.保持会话活跃。在PHP中防范会话劫持的方法包括:1.使用session_regenerate_id()函数重新生成会话ID,2.通过数据库存储会话数据,3.确保所有会话数据通过HTTPS传输。

在PHP中,异常处理通过try,catch,finally,和throw关键字实现。1)try块包围可能抛出异常的代码;2)catch块处理异常;3)finally块确保代码始终执行;4)throw用于手动抛出异常。这些机制帮助提升代码的健壮性和可维护性。

在PHP中,include,require,include_once,require_once的区别在于:1)include产生警告并继续执行,2)require产生致命错误并停止执行,3)include_once和require_once防止重复包含。这些函数的选择取决于文件的重要性和是否需要防止重复包含,合理使用可以提高代码的可读性和可维护性。

PHP中有四种主要错误类型:1.Notice:最轻微,不会中断程序,如访问未定义变量;2.Warning:比Notice严重,不会终止程序,如包含不存在文件;3.FatalError:最严重,会终止程序,如调用不存在函数;4.ParseError:语法错误,会阻止程序执行,如忘记添加结束标签。

PHP和Python各有优势,选择依据项目需求。1.PHP适合web开发,尤其快速开发和维护网站。2.Python适用于数据科学、机器学习和人工智能,语法简洁,适合初学者。

HTTP请求方法包括GET、POST、PUT和DELETE,分别用于获取、提交、更新和删除资源。1.GET方法用于获取资源,适用于读取操作。2.POST方法用于提交数据,常用于创建新资源。3.PUT方法用于更新资源,适用于完整更新。4.DELETE方法用于删除资源,适用于删除操作。

PHP是一种广泛应用于服务器端的脚本语言,特别适合web开发。1.PHP可以嵌入HTML,处理HTTP请求和响应,支持多种数据库。2.PHP用于生成动态网页内容,处理表单数据,访问数据库等,具有强大的社区支持和开源资源。3.PHP是解释型语言,执行过程包括词法分析、语法分析、编译和执行。4.PHP可以与MySQL结合用于用户注册系统等高级应用。5.调试PHP时,可使用error_reporting()和var_dump()等函数。6.优化PHP代码可通过缓存机制、优化数据库查询和使用内置函数。7
