前言
今天复现时发现自己关于phar反序列化了解得不够,特地记录一下加深印象。
前置知识
a stub
可以理解为一个标志,格式为xxx<?php xxx;__HALT_COMPILER();?>
,前面内容不限,但必须以__HALT_COMPILER();?>
来结尾,否则phar扩展将无法识别这个文件为phar文件。
phar文件组成
这里重点关注箭头部分,它会以序列化的形式储存用户自定义的meta-data,这也是我们利用的核心。
触发反序列化方式
既然.phar文件中存在可控的序列化内容,为了重写对象我们自然要想办法让它反序列化,在php中大部分系统函数在通过phar://
伪协议解析phar文件时,会将meta-data
反序列化:
也就是说,当存在file_get_contents($_GET[‘filename’])类似函数的时候,meta-data中的内容会自行反序列化,不需要反序列化函数,这也是phar反序列化的一个优点。
简单测试
首先我们需要生成一个.phar文件,php中存在Phar类实现相关操作。
注意:要生成.phar文件,需要先将php.ini中的phar.readonly设置成Off,并将前面的;去掉(php5.2没有该配置项)
1 | //phar.php => 生成phar.phar |
1 | // phar_unser.php => 自动反序列化重写对象 |
访问phar.php生成phar.phar,因为这里包含了phar.phar,直接访问phar_unser.php,成功输出构造内容:
phar文件上传绕过
之前提到过phar拓展通过结尾的__HALT_COMPILER();?>
来识别是否为phar文件,与之前的内容和后缀名都无关。于是我们可以通过添加文件头+修改文件后缀来绕过一些文件类型限制。比较常见的是伪装成GIF图片。
首先我们搭建一个漏洞环境,限制只允许上传gif类型图片,然后再写一个有析构函数的可以利用的类:
1 | //upload_file.php |
1 | <!--upload_file.html--> |
1 | //file_un.php 可利用的代码 |
注意:没有file_exist($filename)
去解析伪协议(或者类似函数,上文有提到)是不会自动反序列化的。
然后根据上面可利用的代码编写PoC(这个框架用得很多):
1 |
|
利用phar://
伪协议访问相应文件成功执行phpinfo()
命令:
PHP反序列化再巩固
我理解的PHP应该是:向用户可控的unserialize()函数中,传入一个含有析构函数的类的对象的序列化字符串(通常是精心构造的),使其反序列化为对象之后执行析构函数,达到我们的一些目的。
CISCN 2019 DropBox
首先通过抓包fuzz,发现存在任意文件下载漏洞, 把源码下载下来。
这里主要看delete.php和class.php
1 | //delete.php |
单看delete.php
发现不了什么,只是一个简单的删除文件功能,这里注意到$filename
是我们可控的,另外新建了$file的对象,调用了open()函数;接着看class.php
:
1 |
|
跟进File类的open()方法:
1 | public function open($filename) { |
发现存在file_exists($filename)
,根据前置知识可以知道,这里的$filename
如果我们传入phar://
伪协议的话会触发反序列化,接下来的问题就是寻找利用点。
同样在File类,发现存在$this->close()
方法可以读取文件:
1 | public function close() { |
在FileList类中,有__call()魔法函数,当FileList类的实例调用本类不存在的方法时,File类的$file实例将会调用该方法:
1 | class FileList { |
再看User类,发现它最后会调用一个close()方法:
1 | class User { |
整理思路:实例化User类,令$db为一个FileList类的对象,但是FileList类没有close()方法,将会调用__call()方法,而call()方法会调用File对象的close()方法,重写$filename,使file_get_contents($filename)读取指定文件,最后FileList对象调用__destruct(),将函数执行结果打印出来。
注意:这里可能有人和我之前想的一样:直接让$db赋值为File对象不就行了?但是这样不会调用__destruct()将函数读取的内容返回,所以我们必须要利用到FileList对象
Exp:
1 |
|
删除文件时抓包,phar://协议访问指定文件读取flag:
PS:php://filter/resource=phar://
之类的伪协议可以用来绕过限制开头不能为phar://
(参考SUCTF upload 2),这里尝试失败,但还是记录一下。
参考链接
小结
复现 SUCTF Upload 2失败2333,还是tcl。