核心原理
OPCache 是 PHP 官方扩展,用于缓存 PHP 脚本编译后的 opcode(中间代码),避免每次请求时重复编译,提升运行效率。其工作流程为:首次访问 PHP 文件时,PHP 解析器编译脚本生成 opcode,OPCache 将 opcode 及相关元数据(如脚本路径、时间戳、校验和等)存入缓存文件;后续访问同一文件时,直接读取缓存文件执行,无需重新编译。
当满足以下条件时,攻击者可利用 OPCACHE 缓存文件实现远程代码执行(RCE):
-
缓存文件可预测与伪造:OPCache 缓存文件存储路径由 system_id
(基于 PHP 版本、Zend 扩展 ID、系统架构等生成的唯一标识)和目标脚本路径拼接而成,若能获取system_id
,即可确定缓存文件路径。 -
时间戳验证可控:OPCache 默认开启时间戳验证( opcache.validate_timestamps=On
),缓存文件的时间戳需与原 PHP 文件一致。若攻击者能获取原文件时间戳(如通过文件下载、目录遍历等),可修改伪造缓存文件的时间戳绕过验证。 -
权限与配置支持: opcache.validate_root
和opcache.validate_permission
需为Off
(默认值),允许 OPCACHE 读取非网站根目录或非授权路径的缓存文件;同时需开启文件缓存(opcache.file_cache
指定缓存目录,如/tmp
)且仅使用文件缓存(opcache.file_cache_only=1
)
环境搭建
我们首先拉一个普通的phpx-apache镜像下来就行了命令如下
docker pull php:7.4-apache
使用镜像生成临时容器 ,名为:test_php7.4
docker run -it --name test_php7.4apache -d -p 8000:80 php:7.4-apache
其中-p 用来进行端口映射 将容器内的80端口映射到本机的8000端口
进入我们创建的临时容器
docker exec -it test_php7.4apache /bin/bash
执行下命令 为容器安装opcache扩展
docker-php-ext-configure opcache --enable-opcache && docker-php-ext-install opcache
然后在容器内重启一下apache
如果是 exec 进入容器,修改 Apache 之后需要重启服务器,在容器内使用
service apache2 restart
将会导致退出容器。采用下面两种方式可以不退出容器而更新 Apache。
方法一:在容器内执行
service apache2 reload
方法二:exit 退出容器后,执行:
docker restart [container name]
成功安装了opcache拓展了。
Opcache-PHP7
往ini文件添加如下配置,然后看一下phpinfo界面
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
[opcache]
opcache.enable=1
opcache.file_cache="/tmp"
opcache.file_cache_only=1
在此之前 我们要先找到容器中php.ini存在的位置
使用命令php -i |grep php.ini
会得到如下结果
Configuration File (php.ini) Path => /usr/local/etc/php
于是进到/usr/local/etc/php
目录,发现只有php.ini-development
、php.ini-production文件以及conf
目录,随机查看了php.ini-development
文件内容,基本就是PHP配置文件,于是我们复制一份出来,输入以下命令
cp /usr/local/etc/php/php.ini-development /usr/local/etc/php/php.ini
然后我们当前目录下就有php.ini 接下来接着我们之前的修改 但php容器里面默认什么编辑器都没有 就需要我们去下一个 我安装的是vim
这里先用apt update
更新一下安装源
apt-get install vim
然后下载vim
就更改我们的php.ini文件
更改后用我们上传的容器里重启apache2的命令 接着打开phpinfo页面 可以发现opcache的目录变成tmp了
可以看见缓存文件夹是b02875caf1823bb9c08a184ff7eb206f,怎么去算这个system_id呢?
克隆工具仓库 git clone https://github.com/GoSecure/php7-opcache-override
,进入目录后执行 Python 脚本
发现不同,我们换为php7.1试试看
成功
进入容器 /tmp
目录,执行 ls
,可看到以 System ID
命名的目录(如 1116d566fdc53f79abce6c01e3a0308d
),进入该目录下的 var/www/html
(与网站根目录路径一致),可发现 info.php.bin
OPCache 会验证缓存文件与原文件的时间戳一致性,需先获取目标文件(如 info.php
)的时间戳,
-
容器内查看:进入 /var/www/html
,执行stat info.php
,获取Modify
对应的时间戳(如1699999999
)。 -
远程获取:若有文件下载或目录遍历漏洞,可直接下载文件后本地查看时间戳;或通过脚本读取文件的 filemtime()
结果(如<?php echo filemtime('info.php'); ?>
)。 -
然后我们去自己的服务器生成一个一句话木马的缓存bin文件:
按上面的改
Opcache-PHP8
PHP8 对 system_id
的生成算法进行了调整,原 PHP7 的 system_id_scraper.py
脚本失效,需重新计算 system_id
:
1. 计算 PHP8 的 system_id
PHP8 的 system_id
由 PHP 版本 + Zend 扩展 ID + BIN 标识 + 架构信息
拼接后通过 MD5 哈希生成,例如:
-
已知目标 PHP 版本为 8.2.13,Zend 扩展 ID 为 API420220829,NTS
,BIN 标识为BIN_4888
,架构为 64 位(size_t
占 8 字节),则拼接字符串为8.2.13API420220829,NTSBIN_4888(size_t)8\002
。 -
执行 PHP 代码计算 MD5 哈希: <?php var_dump(md5("8.2.13API420220829,NTSBIN_4888(size_t)8\002")); ?>
输出结果(如55fe1f60393626d06ae923abe2bad895
) 即为system_id
。
2. 后续利用步骤
还是先搭一下环境把呜呜呜
计算md5哈希
ounter(lineounter(line
<?php
var_dump(md5("8.2.13API420220829,NTSBIN_4888(size_t)8\002"));
PHP8 的缓存文件结构、时间戳验证逻辑与 PHP7 一致,获取 system_id
后,后续 “获取时间戳 → 伪造缓存文件 → 上传覆盖 → 触发 RCE” 的步骤与 PHP7 完全相同
关键注意事项
-
版本兼容性:PHP7 与 PHP8 的
system_id
计算方式不同,需区分目标版本选择对应工具或脚本。 -
配置依赖:必须确保
opcache.file_cache
目录可写、opcache.file_cache_only=1
、opcache.validate_root=Off
、opcache.validate_permission=Off
,否则无法成功伪造缓存。 -
时间戳准确性:时间戳需精确到秒(部分版本支持毫秒),若伪造文件时间戳与原文件不一致,OPCache 会忽略缓存文件,重新编译原脚本,导致利用失败。
-
缓存文件结构:缓存文件包含
magic
(固定为OPCACHE
)、system_id
、mem_size
、timestamp
等字段,伪造时需保证字段格式正确(可通过 010 Editor 加载 OPCache 模板校验结构)。
本站收集的资源仅供内部学习研究软件设计思想和原理使用,学习研究后请自觉删除,请勿传播,因未及时删除所造成的任何后果责任自负。
如果用于其他用途,请购买正版支持作者,谢谢!若您认为「WWW.HACKNOTE.COM.CN」发布的内容若侵犯到您的权益,请联系站长邮箱:crayon@hacknote.com.cn 进行删除处理。
本站资源大多存储在云盘,如发现链接失效,请联系我们,我们会第一时间更新。
暂无评论内容