反序列化漏洞详解(第一期):从基础认知到原理拆解
反序列化漏洞详解(第一期):从基础认知到原理拆解
摘要:反序列化漏洞是Web安全领域中危害极高、隐蔽性极强的漏洞类型之一,也是渗透测试、安全运维中的核心重点,log4j2、fastjson等知名应用的漏洞中都不乏它的身影。
本系列文章分为两期,第一期聚焦反序列化漏洞的基础认知、核心原理、序列化与反序列化的本质,拆解Java、PHP两大主流语言的序列化机制与漏洞触发前提,帮助零基础新手快速入门,建立反序列化漏洞的核心认知,为第二期的漏洞利用、靶场实操与防御措施做好铺垫。
一、前言:为什么反序列化漏洞值得重点学习?
在Web安全领域,反序列化漏洞之所以被称为“高危漏洞之王”,核心原因在于其危害范围广、利用门槛相对较低,且隐蔽性极强——它不像SQL注入、XSS漏洞那样有明显的输入输出反馈,往往隐藏在程序的底层逻辑中,一旦被利用,可能直接导致远程代码执行(RCE)、服务器控制权被夺取,甚至引发内网横向渗透、核心数据泄露等严重后果。
无论是企业安全测试、渗透测试工程师岗位,还是CTF竞赛,反序列化漏洞都是高频考点与必测项。很多新手在学习时,容易被“序列化”“反序列化”的概念劝退,其实只要理清核心逻辑,从基础原理入手,就能快速掌握其本质。
本系列文章将分两期,循序渐进讲解反序列化漏洞:
第一期(本文):基础认知 → 序列化与反序列化本质 → 主流语言(Java/PHP)序列化机制 → 漏洞触发核心前提;
第二期:漏洞利用原理 → 靶场实操(DVWA、Vulhub)→ 常见利用工具 → 基础防御措施。
建议新手先吃透第一期的基础内容,再进入第二期的实操学习,避免因基础薄弱导致实操困难。同时需坚守法律法规和职业道德,所有技术学习仅用于合法的安全测试与防护,严禁利用所学技术从事非法攻击活动。
二、核心基础:搞懂序列化与反序列化(必学)
要理解反序列化漏洞,首先必须明确两个核心概念:序列化(Serialization)和反序列化(Deserialization)。这两个操作是程序开发中常用的数据处理方式,漏洞的产生,本质就是这两个操作的“不规范使用”。
2.1 什么是序列化?
序列化,简单来说,就是将程序内存中的“对象”(包含数据和方法),转换为可存储、可传输的格式(如二进制流、字符串、JSON、XML等)的过程。
举个通俗的例子:我们在使用社交软件时,发送的消息、上传的文件,本质上都是程序中的“对象”,这些对象无法直接在网络中传输,也无法直接保存到本地文件或数据库中。此时,程序会通过序列化操作,将这些对象“打包”成一种通用的格式(比如二进制流),然后再进行传输或存储——这个“打包”的过程,就是序列化。
核心目的:实现对象的“跨场景传递”(网络传输、本地存储),确保对象在不同环境、不同程序中,能够被正确识别和使用。
常见场景:
用户登录后,服务器将用户信息(对象)序列化,保存到Session中,维持用户会话;
分布式系统中,服务之间传递数据(如订单信息、用户数据),需先序列化;
程序将复杂数据(如列表、对象)保存到本地文件或数据库,需通过序列化转换格式。
2.2 什么是反序列化?
反序列化,是序列化的“逆操作”——将序列化后的“打包数据”(如二进制流、字符串),还原为程序内存中原始对象的过程。
继续用上面的例子:社交软件接收消息时,会将网络中传输的“二进制流”(序列化后的数据),通过反序列化操作,还原为原始的消息对象,然后展示给用户;服务器读取Session中的序列化数据时,会通过反序列化,还原为用户信息对象,验证用户身份。
核心目的:还原序列化的数据,让程序能够正常使用这些数据(对象),实现数据的“复用”。
关键注意点:反序列化的过程,会自动触发对象中的某些方法(如Java的readObject()、PHP的__wakeup()),这也是反序列化漏洞产生的核心关键——后续会详细拆解。
2.3 序列化与反序列化的正常流程(无漏洞)
正常情况下,序列化与反序列化的流程是安全的,核心流程如下:
程序A创建一个对象(如用户信息对象,包含用户名、密码等数据);
程序A对该对象进行序列化,转换为可传输/存储的格式(如二进制流);
序列化后的数据,通过网络传输到程序B,或保存到本地文件/数据库;
程序B读取序列化数据,对其进行反序列化,还原为原始的对象;
程序B正常使用还原后的对象,完成业务操作(如验证用户身份、展示数据)。
这个流程的核心安全前提是:序列化的数据是“可信的”(来自程序自身或合法的数据源),且反序列化过程中,程序会对数据进行严格校验,不会执行非预期的操作。
三、漏洞核心:反序列化漏洞的本质的触发前提
反序列化漏洞的本质很简单:程序在反序列化过程中,未对输入的序列化数据进行严格校验,导致攻击者可以构造恶意的序列化数据,诱导程序执行非预期的操作(如远程代码执行、数据篡改、拒绝服务等)。
简单来说,就是程序“信任”了不可信的序列化数据,将恶意数据还原为对象后,触发了对象中隐藏的恶意逻辑,最终导致漏洞利用。
3.1 漏洞触发的3个核心前提(缺一不可)
反序列化漏洞并非所有程序都会存在,必须同时满足以下3个前提,漏洞才可能被利用,这也是渗透测试中寻找反序列化漏洞的核心判断依据:
前提1:程序存在反序列化操作
程序必须有接收外部输入的序列化数据,并对其进行反序列化的操作。如果程序仅进行序列化(如只保存数据,不读取还原),或仅处理内部生成的序列化数据(不接收外部输入),则不会存在反序列化漏洞。
常见的反序列化入口:
HTTP请求参数(如POST请求中的参数、Cookie中的Session数据);
文件上传(如上传包含序列化数据的文件,程序读取后反序列化);
分布式系统中的服务间通信(如RMI、Dubbo协议传递的序列化数据);
本地文件/数据库读取(如读取包含序列化数据的日志文件、数据库字段)。
前提2:反序列化的输入数据可被攻击者控制
攻击者必须能够修改或替换反序列化的输入数据——这是漏洞利用的核心。如果程序反序列化的数据是固定的、不可修改的(如内部生成的序列化数据),即使存在反序列化操作,攻击者也无法构造恶意数据,漏洞无法利用。
例如:程序从Cookie中读取序列化的用户信息,若Cookie未加密、未签名,攻击者就可以修改Cookie中的序列化数据,构造恶意对象,触发漏洞。
前提3:反序列化过程中存在可被利用的危险逻辑
这是漏洞产生的“核心诱因”。反序列化过程中,程序会自动触发对象中的某些方法(如Java的readObject()、PHP的魔术方法),如果这些方法中包含危险操作(如执行系统命令、写入恶意文件、读取敏感数据),且操作的参数可被攻击者控制,就会导致漏洞被利用。
简单来说:反序列化本身不产生漏洞,是“反序列化+危险逻辑”的组合,才导致了漏洞的产生。例如,Java中某个类重写了readObject()方法,该方法中包含Runtime.getRuntime().exec()(执行系统命令),且命令参数可被攻击者控制,那么反序列化该类的恶意对象时,就会执行恶意命令。
3.2 反序列化漏洞的核心危害(新手必知)
反序列化漏洞的危害程度极高,仅次于缓冲区溢出漏洞,核心危害主要有4类,新手需重点掌握:
远程代码执行(RCE):最严重的危害,攻击者可通过构造恶意序列化数据,执行系统命令(如反弹shell、删除文件、修改配置),直接夺取服务器控制权,这也是反序列化漏洞最常见的利用目标;
数据篡改与泄露:攻击者可修改反序列化后的对象属性(如将普通用户改为管理员),绕过身份验证,或读取服务器中的敏感数据(如数据库密码、用户信息);
拒绝服务攻击(DoS):构造畸形的序列化数据,导致程序反序列化时出现异常(如无限循环、内存溢出),使服务崩溃,无法正常提供服务;
权限提升:利用反序列化漏洞执行高权限操作,突破程序的权限控制,获取更高的系统权限(如从普通用户提升为root用户)。
四、主流语言:Java与PHP的序列化机制(重点)
反序列化漏洞的利用方式,与编程语言的序列化机制密切相关——不同语言的序列化格式、触发方法不同,漏洞的表现形式也不同。其中,Java和PHP是Web开发中最常用的两大语言,也是反序列化漏洞的高发语言,本期重点拆解这两种语言的核心机制(为第二期实操铺垫)。
4.1 Java序列化与反序列化机制
Java作为面向对象的编译型语言,其序列化机制相对严谨,序列化格式为二进制流(不可直接阅读),漏洞触发依赖特定的方法和利用链,也是企业级应用中反序列化漏洞的主要高发场景。
4.1.1 Java序列化的核心要求
Java中,一个类要能被序列化,必须满足两个核心条件:
该类必须实现java.io.Serializable接口(标记接口,无具体方法,仅用于标记该类可序列化);
类中所有非静态属性(static)、瞬态属性(transient),不会被序列化(仅序列化对象的非静态、非瞬态属性)。
4.1.2 Java序列化与反序列化的核心方法
Java通过两个核心类和方法,实现序列化与反序列化:
序列化:通过ObjectOutputStream类的writeObject()方法,将对象转换为二进制流;
反序列化:通过ObjectInputStream类的readObject()方法,将二进制流还原为对象。
关键重点:如果一个类重写了readObject()方法,那么反序列化该类的对象时,会优先执行重写后的readObject()方法——这是Java反序列化漏洞的核心触发点。攻击者可通过重写readObject()方法,植入恶意逻辑,构造恶意序列化数据,触发漏洞。
示例代码(简单理解):
// 实现Serializable接口,可被序列化 public class User implements Serializable { private String username; private String password; // 重写readObject()方法,植入恶意逻辑 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // 先执行默认的反序列化操作 in.defaultReadObject(); // 恶意逻辑:执行系统命令(calc.exe,弹出计算器) Runtime.getRuntime().exec("calc.exe"); } // getter/setter方法(省略) } // 序列化操作 User user = new User(); user.setUsername("admin"); user.setPassword("123456"); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser")); oos.writeObject(user); // 将user对象序列化,保存到user.ser文件 // 反序列化操作(触发恶意逻辑) ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser")); User user2 = (User) ois.readObject(); // 反序列化时,执行重写的readObject(),弹出计算器注:上述代码仅用于演示原理,实际漏洞场景中,恶意逻辑会更隐蔽,且命令参数可被攻击者控制。
4.1.3 Java反序列化漏洞的核心特点
序列化格式为二进制流,无法直接修改,需通过工具(如ysoserial)生成恶意序列化数据;
漏洞触发依赖readObject()方法的重写,或通过“利用链(Gadget Chain)”串联多个类的方法,最终触发恶意代码执行——Java的安全设计较严谨,单个类的readObject()通常无法直接执行命令,需构造利用链;
高发于Java框架和第三方库(如Apache Commons Collections、Struts2、WebLogic),典型漏洞如CVE-2015-7501(Apache Commons Collections反序列化漏洞)、CVE-2017-10271(WebLogic T3协议反序列化漏洞)。
4.2 PHP序列化与反序列化机制
PHP作为脚本语言,其序列化机制相对简单,序列化格式为明文可阅读的字符串,漏洞触发高度依赖PHP的“魔术方法”,门槛较低,是新手入门反序列化漏洞的首选学习场景。
4.2.1 PHP序列化的格式特点
PHP中,对象序列化后会生成明文字符串,格式清晰,便于攻击者构造恶意数据,格式如下(核心格式):
// 格式:O:类名长度:"类名":属性个数:{属性1类型:属性1长度:"属性1值";属性2类型:属性2长度:"属性2值";...} // 示例:User类(类名长度5),2个属性(username、password) O:5:"User":2:{s:8:"username";s:5:"admin";s:8:"password";s:6:"123456";}格式解析(新手必懂):
O:表示对象(Object);
5:表示类名长度(“User”长度为5);
“User”:表示类名;
2:表示对象的属性个数;
s:表示字符串类型(string),后续数字表示字符串长度;
{}内:表示对象的属性及对应值(属性类型:属性长度:属性值)。
4.2.2 PHP序列化与反序列化的核心函数
PHP通过两个核心函数,实现序列化与反序列化:
序列化:serialize()函数,将对象转换为上述格式的字符串;
反序列化:unserialize()函数,将序列化字符串还原为对象。
关键重点:PHP在反序列化过程中,会自动触发特定的“魔术方法”(预定义方法,以__开头),若这些魔术方法中包含危险操作(如eval()、system()、文件写入),且操作参数可被攻击者控制,就会触发反序列化漏洞。
4.2.3 PHP反序列化漏洞的核心触发点:魔术方法
PHP中,与反序列化漏洞相关的核心魔术方法(新手重点记):
__wakeup():反序列化之前触发(最核心的触发点之一);
__destruct():对象被销毁时触发(反序列化后,若对象无引用,会自动触发);
__toString():对象被当作字符串使用时触发(如echo $obj、字符串拼接);
__call():调用不存在的方法时触发;
__get():访问不存在的属性时触发。
示例代码(简单理解):
// 定义一个包含危险魔术方法的类 class User { private $cmd; // 魔术方法:反序列化前触发 public function __wakeup() { // 危险操作:执行系统命令,cmd参数可控 system($this->cmd); } // 魔术方法:对象销毁时触发 public function __destruct() { // 危险操作:写入恶意文件 file_put_contents("shell.php", "<?php eval(\$_POST['cmd']);?>"); } } // 序列化操作(正常场景) $user = new User(); $user->cmd = "whoami"; $serialized = serialize($user); // 生成序列化字符串 echo $serialized; // 反序列化操作(触发漏洞) $unserialized = unserialize($serialized); // 反序列化时,触发__wakeup(),执行whoami命令;对象销毁时,触发 __destruct(),写入webshell注:上述代码中,若攻击者能控制序列化字符串中的$cmd参数,即可执行任意系统命令,或写入恶意webshell,实现漏洞利用。
4.2.4 PHP反序列化漏洞的核心特点
序列化格式为明文字符串,攻击者可直接修改序列化数据,构造恶意对象,门槛较低;
漏洞触发依赖魔术方法中的危险操作,无需复杂的利用链,新手易上手;
高发于PHP框架(如ThinkPHP、Laravel)和自定义开发的PHP程序,典型漏洞如ThinkPHP5.x反序列化漏洞。
五、本期总结:核心知识点梳理(新手必背)
本期作为反序列化漏洞的基础篇,核心是帮助大家建立基础认知,理清原理,为第二期的实操学习做好铺垫,核心知识点梳理如下,建议新手收藏记忆:
核心概念:序列化(对象→可传输/存储格式),反序列化(可传输/存储格式→对象);
漏洞本质:程序未校验反序列化的输入数据,攻击者构造恶意数据,触发危险逻辑;
触发前提:存在反序列化操作 + 输入数据可控制 + 存在可利用的危险逻辑;
Java机制:需实现Serializable接口,通过readObject()触发漏洞,依赖利用链,格式为二进制流;
PHP机制:通过serialize()/unserialize()函数,依赖魔术方法触发漏洞,格式为明文字符串。
下期预告:第二期将聚焦反序列化漏洞的实战利用,拆解Java、PHP反序列化漏洞的具体利用方法,结合DVWA、Vulhub靶场进行实操演示,讲解漏洞利用工具的使用,以及基础的防御措施,帮助大家将本期所学的基础原理,转化为实战能力。
网安学习资源
网上虽然也有很多的学习资源,但基本上都残缺不全的,这是我们和网安大厂360共同研发的的网安视频教程,内容涵盖了入门必备的操作系统、计算机网络和编程语言等初级知识,而且包含了中级的各种渗透技术,并且还有后期的CTF对抗、区块链安全等高阶技术。总共200多节视频,100多本网安电子书,最新学习路线图和工具安装包都有,不用担心学不全。
🐵这些东西我都可以免费分享给大家,需要的可以点这里自取👉:网安入门到进阶资源
