YII 2 反序列化挖掘与分析
字数 1565 2025-08-30 06:50:35

YII 2 反序列化漏洞挖掘与分析

环境部署

  1. 选择 YII 2 的 basic 包
  2. 修改 config/web.php 文件,将 cookieValidationKey 的值改为 demo
  3. 访问 /web/index.php
  4. controllers\SiteController.php 中的 actionIndex 方法添加反序列化入口

反序列化漏洞一(版本限制:<=2.0.39)

漏洞分析

  1. 反序列化起点SebastianBergmann\RecursionContext\Context 类的 __destruct() 方法

    • 该方法包含循环调用逻辑
    • arrays 属性可控,如果赋值为继承了 IteratorAggregate 类的类,遍历时会调用该类的 getIterator() 方法
  2. 关键调用链

    • PHPUnit\Framework\TestSuite 类的 getIterator() 方法
      • 如果 $iteratorFilter 属性赋值为类,可以调用任意类的 __call() 魔术方法
      • $iteratorFilter 属性可控
    • Faker\Generator 类的 __call() 方法
      • 调用 format() 方法
      • 存在回调函数调用,$arguments 不可控但 getFormatter() 返回值可控
    • PHPUnit\Framework\MockObject\MockClass#generate() 方法
      • 直接调用 eval 方法
      • $classCode 可控

EXP 编写

利用上述调用链构造恶意序列化数据,最终通过 eval 执行任意代码。

漏洞修复

在 2.0.39 后的版本中:

  • Faker\Generator() 类添加了 __wakeup() 方法
  • $formatters 属性置空,使 getFormatter() 返回值不可控

绕过尝试

理论上可以通过引用绕过 __wakeup() 的置空:

  1. $formatters 与另一个类的属性绑定
  2. Generator()::__wakeup() 后对该属性赋值
  3. 由于反序列化顺序(先属性后方法),可以绕过限制

实际测试中:

  • 发现 Swift_Encoder_QpEncoder 类的 __wakeup 方法对 $safeMap 赋值
  • 通过绑定 $safeMap$formatters,赋值后 $formatters 也有值
  • 但实际应用中难以找到可直接赋值的 __wakeup__destruct 方法

反序列化漏洞二(版本限制:<=2.0.45)

漏洞分析

  1. 调用链变化
    • 使用 vendor/fakerphp/faker/src/Faker/ValidGenerator.php__call() 方法
      • 包含两个回调函数
      • 利用点在第二个回调,需要控制 $this->validator$res
    • 利用 vendor/fakerphp/faker/src/Faker/DefaultGenerator.php 中的 __call() 方法返回自定义值

EXP 编写

基于新的调用链构造恶意序列化数据,实现代码执行。

总结

  1. YII 2 反序列化漏洞存在于多个版本中
  2. 漏洞利用依赖于框架依赖组件的魔术方法调用链
  3. 修复方案主要通过添加 __wakeup() 方法清空关键属性
  4. 理论上存在绕过方法,但实际利用条件较为苛刻

防御建议

  1. 及时升级到最新版本
  2. 避免使用不受信任的序列化数据
  3. 审查项目中使用的第三方依赖组件
  4. 实施输入验证和过滤机制
YII 2 反序列化漏洞挖掘与分析 环境部署 选择 YII 2 的 basic 包 修改 config/web.php 文件,将 cookieValidationKey 的值改为 demo 访问 /web/index.php 在 controllers\SiteController.php 中的 actionIndex 方法添加反序列化入口 反序列化漏洞一(版本限制: <=2.0.39) 漏洞分析 反序列化起点 : SebastianBergmann\RecursionContext\Context 类的 __destruct() 方法 该方法包含循环调用逻辑 arrays 属性可控,如果赋值为继承了 IteratorAggregate 类的类,遍历时会调用该类的 getIterator() 方法 关键调用链 : PHPUnit\Framework\TestSuite 类的 getIterator() 方法 如果 $iteratorFilter 属性赋值为类,可以调用任意类的 __call() 魔术方法 $iteratorFilter 属性可控 Faker\Generator 类的 __call() 方法 调用 format() 方法 存在回调函数调用, $arguments 不可控但 getFormatter() 返回值可控 PHPUnit\Framework\MockObject\MockClass#generate() 方法 直接调用 eval 方法 $classCode 可控 EXP 编写 利用上述调用链构造恶意序列化数据,最终通过 eval 执行任意代码。 漏洞修复 在 2.0.39 后的版本中: Faker\Generator() 类添加了 __wakeup() 方法 将 $formatters 属性置空,使 getFormatter() 返回值不可控 绕过尝试 理论上可以通过引用绕过 __wakeup() 的置空: 将 $formatters 与另一个类的属性绑定 在 Generator()::__wakeup() 后对该属性赋值 由于反序列化顺序(先属性后方法),可以绕过限制 实际测试中: 发现 Swift_Encoder_QpEncoder 类的 __wakeup 方法对 $safeMap 赋值 通过绑定 $safeMap 和 $formatters ,赋值后 $formatters 也有值 但实际应用中难以找到可直接赋值的 __wakeup 或 __destruct 方法 反序列化漏洞二(版本限制: <=2.0.45) 漏洞分析 调用链变化 : 使用 vendor/fakerphp/faker/src/Faker/ValidGenerator.php 的 __call() 方法 包含两个回调函数 利用点在第二个回调,需要控制 $this->validator 和 $res 利用 vendor/fakerphp/faker/src/Faker/DefaultGenerator.php 中的 __call() 方法返回自定义值 EXP 编写 基于新的调用链构造恶意序列化数据,实现代码执行。 总结 YII 2 反序列化漏洞存在于多个版本中 漏洞利用依赖于框架依赖组件的魔术方法调用链 修复方案主要通过添加 __wakeup() 方法清空关键属性 理论上存在绕过方法,但实际利用条件较为苛刻 防御建议 及时升级到最新版本 避免使用不受信任的序列化数据 审查项目中使用的第三方依赖组件 实施输入验证和过滤机制