0x00 重点
参考:https://paper.seebug.org/680/ 就是一顿抄
0x01 前言
通常我们在利用反序列化漏洞的时候,只能将序列化后的字符串传入unserialize(),随着代码安全性越来越高,利用难度也越来越大。
但在不久前的Black Hat上,安全研究员Sam Thomas分享了议题It’s a PHP unserialization vulnerability Jim, but not as we know it,利用phar文件会以序列化的形式存储用户自定义的meta-data这一特性,拓展了php反序列化漏洞的攻击面。
该方法在文件系统函数(file_exists()、is_dir()等)参数可控的情况下,配合phar://伪协议,可以不依赖unserialize()直接进行反序列化操作。
这让一些看起来“人畜无害”的函数变得“暗藏杀机”,下面我们就来了解一下这种攻击手法。
phar:// 跟 php://filter 、data:// 等一样,都是流包装,可以将一组php文件进行打包,可以创建默认执行的stub
0x02 原理分析
0x02.1 a stub
可以理解为一个标志,格式为xxx<?php xxx; __HALT_COMPILER();?>,前面内容不限,但必须以__HALT_COMPILER();?>来结尾,否则phar扩展将无法识别这个文件为phar文件。
0x02.2 官方手册
phar文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。
这部分还会以序列化的形式存储用户自定义的meta-data,这是上述攻击手法最核心的地方。
https://www.php.net/manual/zh/phar.fileformat.phar.php

0x02.3 签名格式
https://www.php.net/manual/zh/phar.fileformat.signature.php


0x03 demo
根据文件结构我们来自己构建一个phar文件,php内置了一个Phar类来处理相关操作
注意:要将 php.ini 中的 phar.readonly 选项设置为Off,否则无法生成phar文件
// 文件名称: phar.php<?phpclass TestObject {}$phar = new Phar("phar.phar"); //后缀名必须为phar$phar->startBuffering();$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub$o = new TestObject();$o->data = 'hello';$phar->setMetadata($o); //将自定义的meta-data存入manifest$phar->addFromString("test.txt", "test"); //添加要压缩的文件//签名自动计算$phar->stopBuffering();?>// 执行完毕以后会在跟目录下生成 phar.phar 这个文件
// phar_test_class.php<?phpclass TestObject {public $data;public function __destruct(){echo $this->data;}}include('phar://phar.phar');?>// 执行结果 会去访问 phar_test_class.php 文件同目录下面的 phar.phar// 输出: hello
0x04 受影响函数列表
php一大部分文件系统函数在通过phar://伪协议解析的时候,都会将meta-data进行反序列化,影响函数如下
| 受影响函数列表 | |||
|---|---|---|---|
| fileatime | filectime | file_exists | file_get_contents |
| file_put_contents | file | filegroup | fopen |
| fileinode | filemtime | fileowner | fileperms |
| is_dir | is_executable | is_file | is_link |
| is_readable | is_writable | is_writeable | parse_ini_file |
| copy | unlink | stat | readfile |
| new SplFileInfo() |
0x05 利用条件
- phar文件要能够上传到服务器端
- 要有可用的魔术方法作为“跳板”来构造
- 受影响函数列表-参数可控,且
:/phar等特殊字符没有被过滤
0x06 将phar伪造成其他格式的文件
在前面分析phar的文件结构时可能会注意到,php识别phar文件是通过其文件头的stub,更确切一点来说是 __HALT_COMPILER();?>这段代码,对前面的内容或者后缀名是没有要求的。
那么我们就可以通过添加任意的文件头+修改后缀名的方式将phar文件伪装成其他格式的文件。
// add_phar.php<?phpclass TestObject {}$phar = new Phar('phar-test.phar');$phar->startBuffering();$phar->setStub('GIF89a'.'<?php __HALT_COMPILER();?>'); //设置stub,增加gif文件头$phar->addFromString('test.txt','test'); //添加要压缩的文件$object = new TestObject();$object->data = 'phpinfo();';$phar->setMetadata($object); //将自定义meta-data存入manifest$phar->stopBuffering();?>// 执行完毕以后,会在文件根目录生成一个 phar-test.phar 文件// 然后我们在把 phar-test.phar 文件 重命名为 phar-test.gif// 文件扩展名为 gif// 文件头为 GIF89a// 那么就可以绕过大部分程序检测了
// phar_test.php<?phpclass TestObject {public $data;public function __destruct(){eval($this->data);}}include('phar://phar-test.gif');?>
然后执行 phar_test.php
注意:phar-test.gif 文件是在 add_phar.php生成并且修改出来的

