June 16, 2023

PHP8 OPCACHE缓存文件导致RCE

环境搭建

https://blog.csdn.net/lggirls/article/details/89395827
我们首先拉一个普通的phpx-apche2下来就行了。然后根据上述文章中的指令安装指定拓展,比如opcache

1
docker-php-ext-configure opcache --enable-opcache && docker-php-ext-install opcache

搭建好后效果如下:
image.png
然后重启一下apache
image.png
成功安装了opcache拓展了。

Opcache-PHP7

首先说一下Opcache rce的原理,Opache是php中一个生成缓存文件的拓展,当我们访问一个php文件时,他会产生一个缓存文件,下次访问该php文件时,就是直接根据缓存文件回显页面了。

1
2
3
4
[opcache]
opcache.enable=1
opcache.file_cache="/tmp"
opcache.file_cache_only=1

往ini文件添加如下配置,然后看一下phpinfo界面
image.png
缓存文件位置是/tmp
image.png
可以拿到缓存文件的格式是bin文件,然后缓存文件夹是8431e96e6adfc8fc75ad38a0f6a7eb4e这个是今天的重点,我们怎么去算这个system_id呢?
在PHP环境下已经有前人给出我们计算脚本的,我们可以根据phpinfo给出的信息算
[https://github.com/GoSecure/php7-opcache-override](https://github.com/GoSecure/php7-opcache-override)
image.png
emmmm是不是发现不想同呢?这是因为版本可能有点高了。不太适用,我们换为php7.0试试看
image.png
image.png
这下对劲了,然后我们怎么利用呢?当我们存在任意文件写入或者覆盖时,我们可以通过覆盖.php.bin文件达到RCE的目的,我们把换成文件下载下来分析一下
image.png
可以看到最开头是OPACHE+systemid,因此假如我们获取到了SYSTEMID,我们就可以伪造一份缓存文件了,但是还需要注意一点
image.png
我们这里的opache拓展开启了timestamp,也就是时间戳验证,那么假如我们创建的文件时间戳不对的话,我们也无法覆盖成功的QWQ,那么就需要题目有一个获取时间的地方,假如可以下载任意文件或者直接获取时间戳,那么我们可以用010editor直接修改。
image.png
我010过期了呜呜呜,这里就直接拿别人的图过来了,趁现在时间下载一个破解版的。下载完了
image.png
其中40h位置代表的就是时间戳了。我们记住这个时间戳,然后我们同样去自己的服务器生成一个一句话木马的缓存bin文件:
(这里重新生成了一下phpinfo的php文件)
image.png
我们将恶意文件的时间戳改为如上
image.png
然后替换掉这个文件。然后访问info.php看看效果:

image.png
这样就成功getshell了,需要注意一下生成恶意bin文件的php版本需要大致吻合,不能差太多。

Opcache-PHP8

在PHP8之后,opcache生成system_id的方法有些许改变,所以之前的脚本是跑不出来了,我们需要做的是
image.png
用脚本算是算不出来了,经过m1sery师傅的动态调试可以知道规律
image.png
image.png
成功算出。

1
2
<?php
var_dump(md5("8.2.6API420220829,NTSBIN_4888(size_t)8\002"));

接下来的手法并没有什么区别。

春秋杯2023-php_again

考点就是opache8的缓存文件利用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php

$action = $_GET['action'];
if (empty($action)) {
highlight_file(__FILE__);
die();
}
switch ($action) {
case 0_0:
phpinfo();
break;
case 0o0_111:
exec('zip -r /tmp/www.zip *');
readfile('/tmp/www.zip');
break;
case 0b0_111:
var_dump(scandir('/var/www/html/'));
break;
case 0x0_555:
file_put_contents('/tmp/tmp.zip',base64_decode($_POST['data']));
break;
case 777_777:
exec('cd /tmp && unzip -o tmp.zip');
break;
}

这个代码不太难理解,首先是一个软链接读文件,然后是unzip解压覆盖文件,满足了所有条件后就可以按照上述步骤进行处理,getshell了。

About this Post

This post is written by Boogipop, licensed under CC BY-NC 4.0.

#CTF#PHP