Phar反序列化
什么是Phar
JAR 是开发 Java 程序一个应用,包括所有的可执行、可访问的文件,都打包进了一个 JAR 文件里,使得部署过程十分简单
phar
是 PHP 里类似于 JAR 的一种打包文件,说白了其本质可以理解为一个压缩包
对于 PHP 5.3 或更高版本,phar
后缀文件时默认开启支持的,可以直接使用
一般使用文件包含中的**phar
伪协议进行读取.phar
文件**
Phar
文件结构
- **
stub phar:
**这是phar
文件标识,格式为xxx<?php xxx;__HALT_COMPILER();?>
(头部信息),这是 PHAR 文件的入口点,当 PHP 解释器加载 PHAR 文件时,会首先执行 Stub 代码 manifest:
压缩文件的属性等信息,以序列化方式存储- **
contents:
**压缩文件的内容 - **
signature:
**签名,放在文件末尾
phar
协议解析文件时,会自动触发对**manifest
字段的序列化字符串进行反序列化**
以下是官方文档,红框部分是要进行利用的序列化部分
phar
文件的压缩和解压缩
1 |
|
Phar
类:Phar
是 PHP 内置的一个类,用于创建、操作和管理 PHAR 文件- **
$phar = new Phar('test2.phar',0,'test2.phar');
**第一个参数test2.phar
是创建的 PHAR 文件名,如果文件不存在会创建一个新的文件,如果文件存在并允许进行修改,会对其进行更新;第二个参数0
表示文件创建的标志,实际应用中会使用如:Phar::CREATE
表示创建新的 PHAR 文件,来指定不同的创建模式;第三个参数test2.phar
指定 PHAR 文件的别名,可以在后续中引用该 PHAR 文件时使用 - **
$phar->buildfromDirectory('f:\0Day');
**指定要打包的目录路径 - **
$phar->setDefaultStub('test.txt','test.txt');
**中setDefaultStub
用于为 PHAR 文件设置默认的 Stub,即默认入口;第一个参数test.txt
是入口文件的路径,第二个参数'test.txt'
是内部文件的路径
1 |
|
- **
$phar = new Phar('test.phar');
**通过new Phar('test.phar')
语句,程序尝试打开名为test.phar
的 Phar 压缩包文件,并将其封装成一个Phar
对象存储在变量$phar
中。如果test.phar
文件不存在,会报错 - **
$phar->extractTo('test');
**调用Phar
对象的extractTo
方法,该方法的作用是将 Phar 压缩包中的所有内容提取到指定的目录;参数'test'
是目标目录,即 Phar 压缩包中的文件和目录会被解压到名为test
的目录下。如果该目录不存在,PHP 会尝试创建它;若已存在,则会将文件解压到该目录内
创建一个.phar
文件的模板
1 |
|
具体解析
1 |
|
Phar
反序列化漏洞原理
manifest
压缩文件的属性等信息,以序列化存储,存在一段序列化的字符串
调用phar
伪协议,可读取.phar
文件
phar
协议解析文件时,会自动触发对manifest
字段的序列化字符串进行反序列化
phar
需要满足 PHP >= 5.2,在php.ini
中将phar.readonly
设为Off
以下是这个漏洞受到影响的函数(即可以使用phar
伪协议读取.phar
文件的函数)
具体题目解析
第一个是漏洞页面index.php
,源码如下
第二个是可以生成phar
文件的页面phar.php
,源码如下
审计代码
1 |
|
- 在漏洞页面
index.php
的源码中,定义了Testobj
这个类,有一个属性output
,和一个__destruct()
魔术方法,会执行output
的内容,这里可以进行RCE执行命令 - 然后是可以GET传参
filename
,并且使用了file_exists()
函数检查文件或目录是否存在,这个函数存在与受影响的函数之中,所以这里存在phar
反序列化漏洞
1 |
|
- 在
phar.php
页面可以生成.phar
文件,源码中同样定义了Testobj
类和output
属性,与index.php
中的一致 - 生成
.phar
文件部分的解析见源码
在index.php
页面中传入GET参数filename
,其值为/etc/passwd
1 |
|
正常会根据文件是否存在得到一个布尔值的回显
因为这里使用的是file_exists
函数,是可以使用phar://
伪协议的,但是可以使用phar
伪协议并不代表一定存在phar
反序列化漏洞,还要看能否触发魔术方法进而可以执行一些操作,在这里就是要触发__destruct()
这个魔术方法进而可以执行命令,所以存在漏洞
访问phar.php
这个页面,然后这个页面中的代码会自动生成一个test.phar
的文件,查看tmp
目录下会发现生成了这个文件
可以使用xxd
命令查看文件内容
可以看到写入了序列化后的数据
1 |
|
接着在index.php
页面中查看这个文件发现是存在的
1 |
|
最后就可以利用phar
伪协议读取这个文件,会自动将序列化部分的数据进行反序列化,然后就会自动触发__destruct()
魔术方法,从而可以执行eval($_GET["a"]);
,也就是可以进行GET传参一个a
进行RCE
1 |
|
Phar
反序列化漏洞使用条件
phar
文件可以上传到服务器端- 要有可用反序列化魔术方法作为跳板(就是可以通过触发魔术方法
__destruct()
或__wakeup()
进行RCE之类的操作) - 要有文件操作函数,如:
file_exists()
,fopen()
,file_get_contents()
- 文件操作函数的参数可控
- 要使用的伪协议
phar://
中的字符和关键字没有被过滤
小知识
使用phar://
伪协议读取文件是不看后缀的
如果服务器对上传的文件有限制,只能上传一些png
、jpg
、gif
等图片文件,我们可以把phar
文件修改成其他的任何一个格式的文件,并且不会受到影响
如下,使用mv
命令将test.phar
文件修改成test.png
文件,改变其后缀名
然后使用phar
伪协议读取test.png
文件仍然是可以执行命令的
1 |
|
所以后缀名是没有影响的,只要可以上传到服务器就行
Phar
反序列化例题
题目分析
index.php
页面源码如下
1 |
|
可以发现定义了一个TestObject()
类,存在一个__destruct()
魔术方法可以获取flag.php
中的flag,然后可以进行传参一个POST参数file
,并使用md5_file
函数读取指定文件的内容,并基于这些内容生成哈希值,提示有一个upload.php
页面,访问发现可以上传文件,并且根据标题可知只能上传图片文件
这里使用md5_file
函数读取指定文件,并且有上传文件的接口,存在phar
反序列化漏洞,可以通过上传一个phar
文件,并在index.php
页面通过POST参数file
读取文件,从而进行反序列化触发__destruct()
魔术方法获取flag,不过指定了上传的文件只能是图片,但是在前面学习的小知识中可知可以将phar
文件后缀改成其他的,并且不受影响,所以可以上传一个图片格式并可以被解析
首先可以先试试POST传参file查看文件
1 |
|
可以看到可以读取到/etc/passwd
文件并输出一段哈希值
这题中满足了以下条件,可以进行phar
反序列化的利用
解题步骤
- **生成一个
phar
文件:**在mate-data
里放置一个包含TestObject()
序列化字符串 - 上传文件:
md5_file
执行伪协议,触发反序列化,从而触发__destruct()
魔术方法执行echo flag
开始解题
生成一个phar
文件
可以在自己本地的PhpStorm
中生成一个phar
文件,不过注意要设置PhpStorm
中的phar.readonly
为Off
,否则会出现以下报错
找到当前使用的php版本的php.ini
文件
直接搜索找到phar.readonly
就行,以下是原本的样子
把On
修改为Off
,还要把前面的分号去掉,修改后如下
然后直接运行以下脚本就可以在脚本的同一目录下生成phar
文件了
1 |
|
然后就是在upload.php
页面上传文件,抓包改后缀和MIME头就行
可以看到成功上传进去了
最后就是使用phar
伪协议读取上传的文件
1 |
|