【POP链PartⅠ】PHP应用程序Symfony的POP链利用过程
字数 1599 2025-08-23 18:31:08

Symfony PHP应用程序POP链利用分析

1. 概述

本文详细分析了Symfony框架中doctrine/doctrine-bundle依赖包的POP(Property-Oriented Programming)反序列化链利用过程。该依赖包已被下载超过1.44亿次,是PHP反序列化漏洞研究的理想目标。

2. 核心概念

2.1 反序列化漏洞基础

PHP反序列化漏洞利用的关键点在于:

  • __wakeup(): 反序列化时自动调用
  • __destruct(): 对象销毁时自动调用
  • __call(): 调用不可访问方法时触发
  • __toString(): 对象被当作字符串使用时调用

2.2 研究目标

  • 任意文件写入
  • 任意文件包含
  • PHP特性利用
  • 弱类型安全问题

3. 研究方法

3.1 入口点定位

查找POP链的步骤:

  1. 搜索__wakeup__unserialize__destruct方法
  2. 排除抛出BadMethodCallException的类
  3. 分析可到达的危险函数调用链

3.2 关键类分析

3.2.1 Doctrine\Common\Cache\Psr6\CacheAdapter

final class CacheAdapter implements CacheItemPoolInterface {
    public function __destruct() {
        $this->commit();
    }
    
    public function commit(): bool {
        // 关键逻辑
    }
}

3.2.2 Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage

class MockFileSessionStorage extends MockArraySessionStorage {
    public function save() {
        file_put_contents($tmp, serialize($data));
        rename($tmp, $path);
    }
}

3.2.3 Symfony\Component\Cache\Adapter\PhpArrayAdapter

class PhpArrayAdapter {
    private function initialize() {
        $values = self::$valuesCache[$this->file] = (include $this->file) ?: [[], []];
    }
}

4. POP链构建过程

4.1 第一阶段:从__destruct到commit

  1. 通过CacheAdapter__destruct方法触发commit调用
  2. commit方法中关键路径:
    • $item->getExpiry()调用
    • $item->get()调用
    • $this->cache->save()调用
    • $this->cache->delete()调用

4.2 第二阶段:文件写入利用

通过MockFileSessionStoragesave方法实现文件写入:

  1. 控制$data内容写入临时文件
  2. 文件路径格式为$savePath/$id.mocksess
  3. 虽然扩展名固定为.mocksess,但可以写入PHP代码

4.3 第三阶段:文件包含利用

通过PhpArrayAdapterdeleteItem触发文件包含:

  1. deleteItem调用initialize方法
  2. initialize方法使用include包含指定文件
  3. 结合前阶段的文件写入实现代码执行

5. 完整利用链

  1. CacheAdapter::__destruct()commit()
  2. 根据条件选择路径:
    • 过期项:delete()deleteItem()initialize()include
    • 未过期项:save() → 文件写入
  3. 通过文件写入+文件包含实现代码执行

6. 技术难点与解决方案

6.1 类型限制绕过

虽然PHP是弱类型语言,但某些接口限制可通过:

  • Traits特性继承
  • 中间代理类
  • 方法调用链跳转

6.2 文件扩展名限制

虽然无法直接创建.php文件,但:

  • 写入的序列化数据可包含PHP代码
  • 通过include执行序列化文件中的代码

7. 防御建议

  1. 避免反序列化用户可控数据
  2. 实现__wakeup()__unserialize()时抛出异常
  3. 对敏感操作进行严格类型检查
  4. 及时更新依赖包版本

8. 总结

该POP链利用展示了:

  1. PHP反序列化的危险性
  2. 弱类型语言的潜在安全问题
  3. 复杂依赖关系中隐藏的攻击面
  4. 通过方法调用链实现权限提升的技巧

通过深入分析此类漏洞,可以更好地理解PHP应用程序的安全机制和防御策略。

Symfony PHP应用程序POP链利用分析 1. 概述 本文详细分析了Symfony框架中 doctrine/doctrine-bundle 依赖包的POP(Property-Oriented Programming)反序列化链利用过程。该依赖包已被下载超过1.44亿次,是PHP反序列化漏洞研究的理想目标。 2. 核心概念 2.1 反序列化漏洞基础 PHP反序列化漏洞利用的关键点在于: __wakeup() : 反序列化时自动调用 __destruct() : 对象销毁时自动调用 __call() : 调用不可访问方法时触发 __toString() : 对象被当作字符串使用时调用 2.2 研究目标 任意文件写入 任意文件包含 PHP特性利用 弱类型安全问题 3. 研究方法 3.1 入口点定位 查找POP链的步骤: 搜索 __wakeup 、 __unserialize 和 __destruct 方法 排除抛出 BadMethodCallException 的类 分析可到达的危险函数调用链 3.2 关键类分析 3.2.1 Doctrine\Common\Cache\Psr6\CacheAdapter 3.2.2 Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage 3.2.3 Symfony\Component\Cache\Adapter\PhpArrayAdapter 4. POP链构建过程 4.1 第一阶段:从__ destruct到commit 通过 CacheAdapter 的 __destruct 方法触发 commit 调用 commit 方法中关键路径: $item->getExpiry() 调用 $item->get() 调用 $this->cache->save() 调用 $this->cache->delete() 调用 4.2 第二阶段:文件写入利用 通过 MockFileSessionStorage 的 save 方法实现文件写入: 控制 $data 内容写入临时文件 文件路径格式为 $savePath/$id.mocksess 虽然扩展名固定为 .mocksess ,但可以写入PHP代码 4.3 第三阶段:文件包含利用 通过 PhpArrayAdapter 的 deleteItem 触发文件包含: deleteItem 调用 initialize 方法 initialize 方法使用 include 包含指定文件 结合前阶段的文件写入实现代码执行 5. 完整利用链 CacheAdapter::__destruct() → commit() 根据条件选择路径: 过期项: delete() → deleteItem() → initialize() → include 未过期项: save() → 文件写入 通过文件写入+文件包含实现代码执行 6. 技术难点与解决方案 6.1 类型限制绕过 虽然PHP是弱类型语言,但某些接口限制可通过: Traits特性继承 中间代理类 方法调用链跳转 6.2 文件扩展名限制 虽然无法直接创建 .php 文件,但: 写入的序列化数据可包含PHP代码 通过 include 执行序列化文件中的代码 7. 防御建议 避免反序列化用户可控数据 实现 __wakeup() 或 __unserialize() 时抛出异常 对敏感操作进行严格类型检查 及时更新依赖包版本 8. 总结 该POP链利用展示了: PHP反序列化的危险性 弱类型语言的潜在安全问题 复杂依赖关系中隐藏的攻击面 通过方法调用链实现权限提升的技巧 通过深入分析此类漏洞,可以更好地理解PHP应用程序的安全机制和防御策略。