Java 反序列化过程深究
字数 1138 2025-08-26 22:11:35
Java 反序列化过程深度解析
0x01 反序列化漏洞基本原理
Java反序列化漏洞与PHP反序列化漏洞类似,其核心在于:
- 开发者重写
readObject方法时插入了漏洞代码 - 在反序列化过程中触发这些漏洞代码
- 类似于PHP中在
__destruct等魔术函数中触发漏洞代码
典型漏洞触发流程:
FileInputStream fis = new FileInputStream("calc.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
Object obj = ois.readObject(); // 触发点
0x02 反序列化调用链深度分析
关键调用流程
-
初始入口:
ObjectInputStream.readObject()- 调用
readObject0(false)方法
- 调用
-
对象类型判断:
readObject0- 当遇到
TC_OBJECT(值为115)时 - 调用
readOrdinaryObject(unshared)
- 当遇到
-
普通对象处理:
readOrdinaryObject- 关键分支:
if (desc.isExternalizable()) { readExternalData((Externalizable) obj, desc); } else { readSerialData(obj, desc); // 主要进入这里 }
- 关键分支:
-
序列化数据处理:
readSerialData- 核心判断逻辑:
if (slotDesc.hasReadObjectMethod()) { slotDesc.invokeReadObject(obj, this); // 重写readObject时进入 } else { defaultReadFields(obj, slotDesc); // 未重写时进入 }
- 核心判断逻辑:
重写与未重写readObject的区别
-
重写readObject的情况:
slotDesc.hasReadObjectMethod()返回true- 进入
invokeReadObject方法 - 通过反射机制调用自定义的readObject方法:
readObjectMethod.invoke(obj, new Object[]{ in });
-
未重写readObject的情况:
slotDesc.hasReadObjectMethod()返回false- 进入
defaultReadFields方法 - 使用默认的反序列化流程
关键判断点分析
hasReadObjectMethod()方法实现:
return (readObjectMethod != null);
调试观察:
- 未重写readObject时:
readObjectMethod为null - 重写readObject时:
readObjectMethod指向自定义方法
0x03 技术总结与要点
-
核心流程:
readObject() → readObject0() → readOrdinaryObject() → readSerialData() ↘ hasReadObjectMethod()? → invokeReadObject()/defaultReadFields() -
关键区别:
- 是否重写readObject决定了最终进入哪个处理分支
- 重写时通过反射机制调用自定义逻辑
- 未重写时使用标准反序列化流程
-
安全启示:
- 重写readObject方法时要特别注意安全性
- 避免在readObject中执行危险操作
- 反序列化漏洞利用的核心在于控制readObject的执行流程
-
防御建议:
- 使用白名单验证反序列化的类
- 考虑使用替代方案如JSON、XML等更安全的序列化格式
- 对不可信的反序列化数据实施严格校验
附录:调试技巧
-
关键断点位置:
- 自定义readObject方法入口
hasReadObjectMethod()返回处invokeReadObject调用处
-
观察变量:
readObjectMethod的值变化slotDesc的内容- 调用栈信息
通过深入理解这些底层机制,可以更好地分析Java反序列化漏洞的原理和利用方式,也能更有效地进行安全防护。