您当前的位置: 首页 >  php

合天网安实验室

暂无认证

  • 0浏览

    0关注

    748博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

thinkphp6的另反序列化分析

合天网安实验室 发布时间:2021-02-05 16:28:34 ,浏览量:0

知识点实操概要

实操探寻ThinkPHP5远程命令执行漏洞形成原因,各种姿势利用方法。

链接指路:https://www.hetianlab.com/expc.do?ec=ECIDb06f-9bfa-4e0a-80af-36af7391a643&pk_campaign=weixin-wemedia    

复制链接马上体验

Forward

之前分析过tp6的一个链;当时是利用__toString方法去进行的中转,从而实现前后两个链的链接,这次是两个另外链条;利用的是可控类下的固定方法进行中转;开始分析;

首先环境可以composer一键搭建,然后php think run进行跑起来就可;

text

首先的想法就是利用析构函数进行最开始的触发;然后一路追踪魔法函数去进行一步一步的推导;首先找到魔法函数在AbstractCache类下;

protected $autosave = true;

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

其代码如上;可以看到autosave可以可控;这里我们可以手动给其复制为false;从而可以触发save方法;

回溯save方法;在CacheStore中找到了save方法;具体代码如下;

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

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

可以看到其调用了getForStorage方法,然后将其赋值给$contents变量。这里追随一下这个方法;

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

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

发现首先调用了cleanContents方法;然后在调用了json_encode方法,这里首先回溯一下cleanContents方法;

    public function cleanContents(array $contents)
    {
        $cachedProperties = array_flip([
            'path', 'dirname', 'basename', 'extension', 'filename',
            'size', 'mimetype', 'visibility', 'timestamp', 'type',
            'md5',
        ]);
    foreach ($contents as $path => $object) {
        if (is_array($object)) {
            $contents[$path] = array_intersect_key($object, $cachedProperties);
        }
    }

    return $contents;
}

首先在这里看到了array_flip方法;这个方法是将数组的键名和键值进行替换;然后数组赋值给$cachedProperties变量;然后将我们传入的参数按照$path和$object的格式来进行各个遍历;然后将键名经过is_array方法的判断如果为true则进行后续的函数处理;否则就直接return $content这个数组;经过这一系列操作完之后,最终是return到了save函数里;然后接着去进行 $this->store->set($this->key, $contents, $this->expire);这里我们发现store也可控;那么就有两种思路,第一个就是去实例化一个有set方法的类,或者我们实例化一个存在__call方法的类;从而可以因为访问不存在的方法去调用到call魔术方法;这里我们先找到一个有set方法的类;在File类中找到:

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   = "\n" . $data;
    $result = file_put_contents($filename, $data);

    if ($result) {
        clearstatcache();
        return true;
    }

    return false;
}

这里可利用点在后面的serialize方法;直接追溯一下;

protected function serialize($data): string
{
    if (is_numeric($data)) {
        return (string) $data;
    }
 
    $serialize = $this->options['serialize'][0] ?? "serialize";
 
    return $serialize($data);
}

这里发现options参量可控;这里就存在一个问题,如果我们将其赋值为system,那么后续return的就是我们命令执行函数,里面的data我们是可以传入的,那么我们就可以实现RCE;

这里放出我自己写的exp;

            
关注
打赏
1665306545
查看更多评论
0.0415s