java反序列化预备全知
字数 1709 2025-08-25 22:58:35
Java反序列化漏洞全面解析
一、Java序列化与反序列化基础
1. Java IO流与序列化关系
Java的IO流分为:
- 文件IO流(FileInput/OutputStream):用于文件输入输出
- 对象IO流(ObjectInput/OutputStream):用于对象输入输出
序列化过程实质上是将对象通过对象输出流(ObjectOutputStream)转换为字节流输出到文件或其他存储介质中。
2. 序列化/反序列化示例代码
序列化示例:
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
反序列化示例:
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
3. 序列化要求
类必须实现Serializable接口:
public class Person implements Serializable {
private String name;
private int age;
// 构造方法和其他方法...
}
4. PHP与Java反序列化区别
| 特性 | PHP | Java |
|---|---|---|
| API | serialize/unserialize | 需自定义序列化过程 |
| 触发方法 | __wakeup | readObject |
| 设计目的 | 初始化对象 | 还原完整对象 |
二、Java反射机制
1. 反射基础
反射允许程序在运行时获取类的信息并操作类或对象,无需在编译时知道具体类。
2. 反射常用方法
Class.forName(classname):获取类的Class对象Class.newInstance():实例化对象(调用无参构造)Class.getMethod(method name, arg types):获取方法Method.invoke(obj, args):调用方法
3. 获取Class对象的三种方式
Class.forName("完整类名")obj.getClass()类名.class
4. 通过反射执行命令
Runtime执行命令:
Class clazz = Class.forName("java.lang.Runtime");
clazz.getMethod("exec", String.class)
.invoke(clazz.getMethod("getRuntime").invoke(clazz), "calc.exe");
ProcessBuilder执行命令:
Class clazz = Class.forName("java.lang.ProcessBuilder");
((ProcessBuilder)clazz.getConstructor(List.class)
.newInstance(Arrays.asList("calc.exe"))).start();
5. 访问私有方法
使用getDeclaredMethod/getDeclaredConstructor配合setAccessible(true):
Constructor m = clazz.getDeclaredConstructor();
m.setAccessible(true);
clazz.getMethod("exec",String.class).invoke(m.newInstance(), "calc.exe");
三、反序列化漏洞利用
1. ysoserial工具
ysoserial是一个集成了多种Java反序列化利用链的工具,可以生成各种payload。
基本使用:
java -jar ysoserial.jar CommonsCollections1 "command"
2. URLDNS利用链分析
URLDNS是简单的反序列化利用链,用于验证反序列化漏洞存在。
利用原理:
- 创建HashMap并将URL对象作为key
- 反序列化时HashMap的readObject方法会调用key的hashCode
- URL的hashCode会触发DNS查询
关键代码:
HashMap ht = new HashMap();
URL u = new URL(null, url, handler);
ht.put(u, url);
Reflections.setFieldValue(u, "hashCode", -1); // 强制重新计算hashCode
3. 调用链分析
HashMap.readObject()HashMap.putVal()HashMap.hash()URL.hashCode()URLStreamHandler.hashCode()URLStreamHandler.getHostAddress()InetAddress.getByName()→ 触发DNS查询
四、代理模式与动态代理
1. 静态代理
角色:
- 抽象角色(接口):如Rent
- 真实角色:如Host
- 代理角色:如Proxy
- 客户:使用代理的代码
示例:
public class Proxy implements Rent {
private Host host;
public void rent() {
host.rent();
seeHouse(); // 额外功能
}
}
2. 动态代理
核心类:
java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler
实现步骤:
- 创建
InvocationHandler实现类 - 使用
Proxy.newProxyInstance创建代理对象 - 通过代理对象调用方法
示例:
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;
public Object getProxy() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
public Object invoke(Object proxy, Method method, Object[] args) {
// 前置处理
Object result = method.invoke(target, args);
// 后置处理
return result;
}
}
五、关键安全要点总结
-
反序列化入口点:任何接受外部序列化数据的地方都可能成为入口
-
危险方法:
readObject是主要触发点,但也要注意readResolve等方法 -
利用链组成:通常需要多个类的组合才能构成完整利用链
-
防御措施:
- 使用白名单验证反序列化的类
- 使用
ObjectInputFilter限制反序列化类 - 升级存在漏洞的库
-
工具使用:ysoserial是研究和验证反序列化漏洞的重要工具
通过深入理解这些知识点,可以更好地分析Java反序列化漏洞并开发相应的防御措施。