[0xGame2025]Rubbish_Unser 详解
什么是序列化
序列化是指将变量转换为一种可保存或传输的字符串形式的过程;
而反序列化则是在需要的时候,将这个字符串重新转换回原来的变量形式以供使用。
这两个过程相辅相成,为数据的存储和传输提供了极大的便利,同时也使得程序更加易于维护和扩展。
初步了解
serialize()是 PHP 中一个非常重要的函数,用于将变量(如数组、对象等)转换为可存储或传输的字符串格式unserialize()是 PHP 中一个极其强大但也极度危险的函数,用于将通过serialize()生成的字符串还原为原始的 PHP 变量(如数组、对象等)
1 |
|
简单来说序列化就是把程序中的数据(比如对象、数组等)转换成一种可以存储或传输的格式(通常是字符串),以便以后能还原回来。
牛刀小试
1 |
|
吓哭了,先来了解一些魔术方法的调用时机然后进行审计
__construct():创建对象时自动调用(构造函数),一般用于初始化__destruct():对象被销毁时(脚本结束、unse)__get($name):访问一个未定义(或不可访问)的属性时__invoke():当把对象当作函数调用时__call($name, $arguments):调用一个不存在(或不可访问)的方法时__toString():对象被当作字符串使用时
命令执行关键点
销毁对象时触发
function __destruct() { echo "破绽,在这里!" . $this -> yuzuha; }1
2
3
4
5
6
7
8
9
- 访问一个未定义(或不可访问)的属性时
- ```PHP
function __get($robin)
{
$castorice = $this -> robin;
eval($castorice);
}
WP
- ZZZ类对象被销毁时触发__destruct(),首先想到实例化一个ZZZ对象
- 我们需要访问一个未定义(或不可访问)的属性时才能触发
__get($name)进行命令执行,那我们就去找符合要求的类,可以看到在HI3rd类对象中完成if判断后会返回guanxing属性的Elysia属性,而在HSR中没有Elysia属性就触发了__get方法,因此我们可以把guanxing属性赋值一个 HSR类的实例 - 如何绕过if条件判断?要求kiana 属性的值和RaidenMei属性的值不同但是要求MD5和哈希值相同,赋值使用的一个为false一个为空字符串来绕过(其实null也可以,都是空值嘛)
- 怎么触发__invoke()?在GI类中我们发现__call方法,把GI类对象中的$furina实例化为HI3rd类对象
- 怎么触发__call()?用一个不存在(或不可访问)的方法时,看到Mi类中的__toString方法中调用了game 属性的tks()方法,我们把$game实例化为GI类对象,该对象没有tks()方法,达成目的
- 最后把ZZZ类对象中的$yuzuha实例化为Mi类对象,调用__destruct()中echo $this->yuzuha时,会触发Mi类中的__toString()方法
- 把逆推的思路重新整理编写脚本
1 |
|
- 执行
- 传递
补充
Q:为什么要
$gar = array('1' => $gen, '2' => null); echo urlencode(serialize($gar));1
2
3
4
5
6
7
而不是直接进行echo urlencode(serialize($gen));?
A:观察原题
```PHP
throw new Exception("Rubbish_Unser");
这会导致主动抛出一个异常(Exception),并终止当前脚本的正常执行流程
异常抛出导致__destruct不执行的绕过代码最后会throw new Exception,导致对象正常销毁流程被打断,__destruct不触发。
构造一个数组$b = array(‘1’ => $a, ‘2’ => null),当数组中某个元素被设为null时,PHP 会提前回收该元素对应的对象,从而在异常抛出前触发__destruct,触发垃圾回收(GC)





