以迷宫类比PHP反序列化链
字数 1648 2025-08-11 08:36:00
PHP反序列化漏洞分析与利用:迷宫类比法
一、核心概念
PHP反序列化漏洞可以类比为一个迷宫:
- 入口:
__destruct()与__toString()方法 - 路径:
__call、同名类、call_user_func_array、call_user_func等方法 - 出口:
call_user_func_array、call_user_func、eval、$a($b,$c)等命令执行与文件写入点
二、反序列化漏洞基础
1. 成因
- 反序列化时将字符串还原成类对象
- 类被销毁时默认触发
__destruct()析构方法 - 类被当作字符串使用时执行
__toString方法
2. 利用条件
- 存在完全可控的反序列化点:
unserialize(可控变量) - 存在文件操作函数且文件名可控:
file_exists('phar://恶意文件') - 注意:PHP8中Phar的元信息不再自动反序列化
三、迷宫入口分析
1. 主要入口方法
-
__destruct():最佳入口,对象销毁时自动调用- 示例:Laravel中的
PendingBroadcast类
class PendingBroadcast { protected $events; protected $event; public function __destruct() { $this->events->dispatch($this->event); } } - 示例:Laravel中的
-
__toString():类被当作字符串时调用- 在WordPress的Guzzle、PHPExcel中有应用
-
__wakeup():执行unserialize()时首先调用
四、路径探索
1. 路径前提
- 类之间存在包含关系
- 框架中更容易实现(遵循composer规则)
2. 关键路径模式
-
模式1:
$this->a->b()或$temp = $this->a; $temp->b()- 要求:
a可控,参数部分或全部可控 b不可控时:定义a为有__call的类public __call(string $name, array $arguments): mixedb可控时:可定义a为任意类,b为该类方法
- 要求:
-
模式2:使用
call_user_func()或call_user_func_array()- 适合二次或多次跳转
3. 方向控制
-
__construct():控制跳转方向- 初始化时可设置参数绕过中间关卡(如if判断)
-
注意事项:
- 只能控制类或父类已定义的变量
- 注意继承和访问修饰符(public/protected/private)
- 父类private变量使用时需在构造时包含父类
__construct和__destruct是类默认存在的
五、出口点
1. 常见出口
-
命令执行:
call_user_func()和call_user_func_array()参数可控时- 形如
$a($b,$c)且$a、$b可控system('id', $c); eval($a)
-
文件操作:
- 文件写入函数
- 文件包含函数
六、实例分析
1. 简单示例代码
class lemon {
protected $ClassObj;
function __construct() {
$this->ClassObj = new normal();
}
function __destruct() {
$this->ClassObj->action();
}
}
class normal {
function action() {
echo "hello";
}
}
class evil {
private $data;
function action() {
eval($this->data);
}
}
unserialize($_GET['d']);
2. 漏洞分析
- 入口:
lemon::__destruct() - 路径:
$this->ClassObj->action()- 原指向
normal::action() - 通过控制
ClassObj指向evil::action()
- 原指向
- 出口:
eval($this->data)
3. 利用构造
class lemon {
protected $ClassObj;
function __construct() {
$this->ClassObj = new evil(); // 控制跳转方向
}
}
class evil {
private $data = "phpinfo();"; // 控制执行代码
}
echo urlencode(serialize(new lemon())); // 编码防止乱码
七、高级技巧
1. 绕过__wakeup
- 通过修改序列化字符串的对象计数
- 示例:
O:4:"test":1:{s:1:"a";s:3:"aaa";}改为O:4:"test":2:{s:1:"a";s:3:"aaa";}
2. 特殊反序列化方式
- phar反序列化:通过
phar://协议触发 - session反序列化:控制session数据
3. 访问修饰符处理
- private和protected属性序列化时包含
%00 - 需要正确编码处理
八、防御建议
- 避免反序列化用户可控数据
- 使用
__wakeup()进行安全检查 - 对敏感操作添加权限验证
- 及时更新PHP版本(特别是PHP8对Phar的改进)
通过这种迷宫类比法,可以系统性地分析PHP反序列化漏洞,明确寻找入口、路径和出口的策略,提高漏洞挖掘和利用的效率。