文章总结: 文章系统介绍了反序列化漏洞的基本概念,重点解析了PHP反序列化漏洞的原理、利用方式和防护措施。文章详细解释了PHP序列化格式、魔术方法的作用机制,以及如何通过构造POP链实现攻击,并提到了利用PHP原生类和第三方框架进行漏洞挖掘的方法。
综合评分: 86
文章分类: 漏洞分析,WEB安全,代码审计,渗透测试
PHP、Java、JavaScrip、python反序列化漏洞大盘点 – PHP漏洞分析
OneDay一卒的老付
老付话安全
2025年8月5日 20:20
山东
点击蓝字
关注我们
始于理论,源于实践,终于实战
老付话安全,每天一点点
激情永无限,进步看得见
严正声明
本号所写文章方法和工具只用于学习和交流,严禁使用文章所述内容中的方法未经许可的情况下对生产系统进行方法验证实施,发生一切问题由相关个人承担法律责任,其与本号无关。
特此声明!!!
前言
什么是序列化?
序列化是将复杂的数据结构(例如对象及其字段)转换为“更扁平”的格式的过程,作为顺序的字节流发送和接收。序列化数据可以:
将复杂数据写入进程间内存、文件或数据库
发送复杂数据,例如,通过网络、应用程序的不同组件之间或在 API 调用中发送
什么是反序列化?
反序列化是将字节流恢复到与原始对象完全相同的功能副本的过程,其状态与序列化时的状态完全相同。然后,网站的逻辑可以与这个反序列化的对象进行交互,就像它与任何其他对象交互一样。
常见的开发PHP & JavaEE &JS& Python都支持序列化和反序列化操作:
上述操作就好比我们通过物流运输大件家具一样,工程为了方便运输节省运费,把一套家具按照一定的规则(说明书)拆分成不同的组件(序列化),物流只打包发送组件即可,当客户收到家具后,按照说明书进行组装还原成实物家具(反序列化),家具本身没有发生变化。这既是通俗讲的序列化与反序列化。
什么是不安全反序列化?
不安全的反序列化是指网站的用户可控数据被反序列化。这可能导致攻击者操纵序列化对象,以便将有害数据传入应用程序代码。甚至用一个完全不同类的对象替换序列化对象。网站可用的任何类的对象都将被反序列化和实例化。因此,不安全的反序列化有时被称为 “对象注入” 漏洞。这就好比我们使用物流发货家具是有一个桌子腿被不法分子替换了,这个替换的桌子腿和原来的一样,不一样的是这个替换的桌子腿内部内置了有危害的东西,当客户安装时没有发现异常而进行了安装,使用时有危害的东西被释放造成安全问题。
不安全的反序列化漏洞是如何产生的?
不安全的反序列化通常是因为普遍缺乏对反序列化用户可控数据可能带来的危险性的理解。理想情况下,用户输入根本不应该被反序列化。但网站认为自己是安全的,因为他们对反序列化的数据实施了某种形式的附加检查。这通常是无效的,因为几乎不可能实施验证或清理来涵盖所有可能的情况。这些检查也存在根本缺陷,因为它们依赖于在数据反序列化后进行检查,这为时已晚,无法防止攻击。说白了,就是可控数据无法完全过滤,导致把危险的内容传输给对方进行反序列化,发序列化解析时把危险内容也解析还行了,导致安全风险。
总结来说,反序列化漏洞的产生是未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致代码执行,SQL注入,目录遍历等不可控后果。 在反序列化的过程中自动触发了开发语言的某些魔术方法。 当进行反序列化的时候就有可能会触发对象中的一些魔术方法。
01
PHP反序列化漏洞
PHP 序列化格式
PHP 使用可读的字符串格式,字母代表数据类型,数字代表每个条目的长度。
例如
//User 对象,其属性为$user->name = "carlos"; $user->isLoggedIn = true;
//序列化后,对象可能为:O:4:"User":2:{s:4:"name":s:6:"carlos"; s:10:"isLoggedIn":b:1;}
O:4:“User” – 类名称为 4 个字符 “User” 的对象
2 – 对象有 2 个属性;
s:4:“name” – 第一个属性的键是 4 个字符的字符串 “name”;
s:6:“carlos” – 第一个属性的值是 6 个字符的字符串 “carlos”;
s:10:“isLoggedIn” – 第二个属性的键是 10 个字符的字符串 “isLoggedIn”;
b:1 – 第二个属性的值是布尔值 true;
PHP 序列化格式示例:
PHP 序列化的原生方法是 serialize() 和 unserialize()。如果有源代码访问权限,可在代码中的任意位置查找 unserialize() 并进一步分析。
在PHP中,调用serialize()函数时,会将对象的属性进行序列化,但不会序列化对象的方法(函数)。这里有个概念,什么是类、对象、属性、方法:
类就是分类,如猫狗是动物类,卡车、货车属于汽车分类,这些类都有一些共同特点,如汽车有颜色、品牌、动力方式等,这些都是汽车分类的属性,属性是类的变量,也就是说颜色这个属性是可以变化的,得给它赋值,颜色=红色就是属性值;属性可以是各种数据类型(如整数、字符串、数组等),每个对象实例都有自己的一组属性,即不同对象的属性值可以不同。这里的对象就是可以新建一个mycar,mycar就是会引用类的属性,它的属性就是颜色“红”,品牌“特斯拉”,动力方式“新能源”。但是可能还有其他描述mycar的信息,如电池大小、车辆配置等,如果增加这些信息得必须现在类里定义这些属性。有了对象和类、属性,它们不是自动关联的,得需要方法(函数)调用来进行关联。
一句话总结:
-
属性是对象的”状态”(数据),方法是对象的”行为”(逻辑)。
-
例如:一辆车的 颜色 是属性(数据),而 启动引擎 是方法(操作)。
对象的方法(函数)有很多种,有原始方法、第三方封装好的方法函数。还有特殊的魔术方法。
魔术方法是不必显式调用的特殊方法子集。相反,当特定事件或场景发生时,它们会自动被调用。魔术方法是各种语言中面向对象编程的常见功能,通过在方法名称前加上前缀或用双下划线来表示。
开发人员可以向类添加魔术方法,以便预先确定在相应的事件或场景发生时应执行哪些代码。魔术方法以双下划线 __ 开头,用于实现对象初始化、属性访问、方法调用、序列化等高级行为。
常见的魔术方法:
__construct(): //当对象new的时候会自动调用
__destruct()://当对象被销毁时会被自动调用
__sleep(): //serialize()执行时被自动调用
__wakeup(): //unserialize()时会被自动调用
__invoke(): //当尝试以调用函数的方法调用一个对象时会被自动调用
__toString(): //把类当作字符串使用时触发
__call(): //调用某个方法,若方法存在,则调用;若不存在,则会去调用__call函数。
__callStatic(): //在静态上下文中调用不可访问的方法时触发
__get(): //读取对象属性时,若存在,则返回属性值;若不存在,则会调用__get函数
__set(): //设置对象的属性时,若属性存在,则赋值;若不存在,则调用__set函数。
__isset(): //在不可访问的属性上调用isset()或empty()触发
__unset(): //在不可访问的属性上使用unset()时触发
__set_state(),调用var_export()导出类时,此静态方法会被调用
__clone(),当对象复制完成时调用
__autoload(),尝试加载未定义的类
__debugInfo(),打印所需调试信息
这些魔法方法需要通过代码演示的方法理解它被触发的条件和触发后的调用过程才能利用它来造成攻击,挖掘漏洞。
PHP反序列化漏洞:
未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致代码执行,SQL注入,目录遍历等不可控后果。在反序列化的过程中自动触发了某些魔术方法。当进行反序列化的时候就有可能会触发对象中的一些魔术方法。
在 PHP 中,POP(Property-Oriented Programming)链是一种利用魔术方法(如 __toString、__destruct、__wakeup 等)和对象属性操控来实现的漏洞利用技术,常用于反序列化(unserialize)漏洞攻击中。其核心思想是通过构造特定的对象链,在反序列化时触发一系列魔术方法,最终执行任意代码或获取敏感信息。
POP链的基本原理
反序列化漏洞:当用户可控的数据被 unserialize() 反序列化时,可能触发对象的魔术方法。
魔术方法的自动调用:如 __wakeup()、__destruct() 会在反序列化或对象销毁时自动执行。
属性操控:通过控制对象的属性,间接调用其他对象的方法(形成调用链)。
攻击流程:
- 入口点:找到一个可控的 unserialize() 调用。
- 魔术方法触发:构造对象链,使反序列化时触发 __wakeup 或 __destruct。
- 链式调用:通过属性传递,从一个对象的魔术方法跳转到另一个对象的方法。
- 最终目标:执行危险操作(如文件写入、代码执行)。
构造POP链的步骤
步骤1:寻找可用的类及其魔术方法
• 分析目标代码中的类,重点关注:
◦ __destruct():对象销毁时触发。
◦ __wakeup():反序列化时触发。
◦ __toString():对象被当作字符串使用时触发。
◦ __call():调用不存在方法时触发。
步骤2:确定调用链(Gadget Chain)
• 找到一个从入口魔术方法(如 __destruct)到危险方法(如 file_put_contents)的调用路径。
• 通过对象的属性($this->xxx)连接多个类的方法。
步骤3:构造Payload
• 序列化精心设计的对象链,使其在反序列化时触发目标方法。
如果在 PHP 类中没有实现魔术方法,那么该魔术方法在相应的情况下不会被自动触发。PHP 的魔术方法需要在类中显式地定义和实现,否则默认情况下不会被调用。当 PHP 在特定情况下需要调用某个魔术方法时(比如创建对象、访问属性、调用方法等),它会检查类是否实现了相应的魔术方法。如果类中没有定义该魔术方法,PHP 将不会触发该方法的自动调用。
在PHP中,定义类的属性时,可以设置不同的变量属性类型,对象的变量属性有
- public(公共的):在本类内部、外部类、子类都可以访问
- protect(受保护的):只有本类或子类或父类中可以访问
- private(私人的):只有本类内部可以使用
这些变量属性类型在序列化时输出的数据是不同的:
- public属性序列化的时候格式是正常成员名
- private属性序列化的时候格式是%00(%00 是空格的意思 ) 类名%00成员名
- protect属性序列化的时候格式是%00*%00成员名
在对对象进行反序列化时,可以很明显的对应处属性值属于哪种属性类型。
除了利用魔术方法挖掘漏洞,还有可以利用PHP原生类,原生类(Native class)是指在编程语言的核心库或标准库中提供的类,这些类是语言本身提供的,而不是由用户自定义的类。原生类通常包含语言内置的功能和特性,用于解决常见的编程任务和操作。在代码中没有看到魔术方法的情况下使用PHP原生类进行漏洞挖掘。
除了原生类可以利用,第三方一些框架也是可以利用的。当知道目标使用了某个框架及对应版本并且这个框架版本曝过反序列漏洞,那么就可以尝试利用该项目去生成反序列链。
今天关于PHP的反序列化漏洞就介绍到这里,有点生涩难懂,需要通过代码演示加深理解。
下一期我们介绍Java反序列化漏洞
END
枯燥乏味的知识点,学起来有点费劲。
但不积跬步无以至千里。
日拱一卒,每天一点点的进步。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:老付话安全 OneDay一卒的老付《PHP、Java、JavaScrip、python反序列化漏洞大盘点 – PHP漏洞分析》