影响版本:Yii2 < 2.0.38
首先搜索反序列化起始点__destruct
魔术方法,在结果的最后一行可以找到存在问题的__destruct
方法
在\vendor\yiisoft\yii2\db\BatchQueryResult.php
文件的79行的__destruct
方法调用了reset方法,reset方法中存在的_dataReader值可控只需访问不存在close的类即可触发__call
魔术方法
调用的利用点如图:
查找可以利用的__call
魔术方法
在文件\vendor\fzaninotto\faker\src\Faker\Generator.php
的283行调用了__call
魔术方法
1 | public function __call($method, $attributes) |
调用了format方法在同文件的226行
1 | public function format($formatter, $arguments = array()) |
这里会调用该文件236行的getFormatter
1 | public function getFormatter($formatter) |
$this->formatters可控 即可以调用任意类的任意方法 但是因为__call中传入的参数不可控 所以需要找到一个可控参数的方法去执行我们的代码 查找全局可控的eval call_user_func 其中找到PHPUnti的eval 还有几个call_user_func 其中Delete Update 和View 这几个文件的方法需要传参
而Create Index则不需要 以Index为例子
在\vendor\yiisoft\yii2\rest\IndexAction.php
76行
1 | public function run() |
checkAccess可控,id可控 即可控制checkAccess为我们的函数 id为我们的参数 即可实现RCE
在issue 有师傅发了另外两个pop链 https://github.com/yiisoft/yii2/issues/18293
其中一个在文件\vendor\codeception\codeception\ext\RunProcess.php
的98行stopProcess 方法中 其中他会遍历processes 数组并赋值给$process变量 然后进行$process->isRunning()
1 | public function stopProcess() |
因为processes 可控 则 process可控当控制访问不存在isRunning()
方法的类时 触发调用__call
魔术方法然后使用我们上面Faker\Generator.php 继续调用任意类中的任意方法 实现RCE
这个是调用yii组件Swift \vendor\swiftmailer\swiftmailer\lib\classes\Swift\KeyCache\DiskKeyCache.php
289行中存在的__destruct
方法
1 | public function __destruct() |
这里调用了225行的clearAll方法 其中keys可控
1 | public function clearAll($nsKey) |
这里有一个is_dir的判断 path的值我们可控 如果控制为一个对象 则触发__toString方法
查找全局可以找到在文件\vendor\phpdocumentor\reflection-docblock\src\DocBlock\Tags\See.php
的79行
1 | public function __toString() : string |
这里面判断了description是否为空 不为空则进行$this->description->render() 因为description我们可控 则可以访问不存在render方法的类 触发__call
魔术方法 接入第一条pop 的Faker\Generator的__call方法 然后CreateAction 中的run方法实现RCE