一文读懂Java内存马——Filter篇
字数 3787 2025-10-18 11:17:50

Java Filter内存马深入解析教学文档

一、前言:什么是内存马?

内存马(Memory Shell)是一种无文件(Fileless)的恶意程序,其核心特征是不将恶意代码持久化到服务器的磁盘文件系统中,而是直接注入并存活于Web服务器进程(如Tomcat、Jetty、Spring等容器)的内存中。这使得它能够绕过传统的基于文件检测的安全机制,具有极高的隐蔽性。

在多种内存马类型中,Filter型内存马因其能够拦截、处理所有经过容器的请求和响应,从而获得强大的远程控制能力,成为攻击者最常用的手段之一。

二、基础认知:Java Web Filter 过滤器

1. Filter 的定义与作用

Filter是Java Servlet规范中定义的一种标准组件,用于在请求到达目标资源(如Servlet、JSP)之前或响应发送给客户端之前,执行预处理或后处理逻辑。你可以将它想象成一个“安检站”或“拦截器”。

Filter的主要作用包括:

  • 认证与授权:检查用户会话(Session),判断是否登录或拥有访问权限。
  • 日志记录:记录请求的详细信息,如IP、URL、时间戳等。
  • 数据转换:统一设置请求(Request)和响应(Response)的字符编码。
  • 安全控制:防御XSS(跨站脚本)、SQL注入等攻击,对输入输出进行过滤。
  • 响应处理:对响应内容进行压缩、加密等。

2. Filter 的工作流程

Filter的执行遵循一个明确的链条模型,称为过滤器链(FilterChain)

  1. 启动加载:Web应用启动时,容器(如Tomcat)会读取 web.xml 配置文件或解析 @WebFilter 注解,实例化配置的Filter,并调用其 init() 方法进行初始化。
  2. 请求拦截:当HTTP请求到达容器时,容器会根据配置的 <url-pattern> 确定需要应用的Filter,并将它们按配置顺序组装成一个FilterChain。
  3. 链式执行
    • 容器依次调用链中每个Filter的 doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 方法。
    • doFilter方法内部,开发者编写处理逻辑。关键操作是调用 chain.doFilter(request, response),此调用会将请求传递给过滤器链中的下一个Filter,最终到达目标Servlet。
    • 如果在某个Filter中不调用 chain.doFilter,则请求在此被阻断,后续的Filter和Servlet都不会执行。
  4. 响应后处理:当 chain.doFilter 调用返回后,执行流程会以“倒序”再次经过每个Filter的 doFilter 方法中 chain.doFilter 调用之后的代码,从而实现对响应的后处理。

三、动手实践:编写一个简单的Filter

理解理论的最佳方式是实践。我们通过一个简单的例子来演示Filter的创建和配置。

目标:创建一个Filter,检查请求参数cmd中是否包含敏感词"script",如果包含,则直接拦截并返回警告信息。

步骤

  1. 创建Filter类
    创建一个Java类,继承自 HttpFilter(或实现 javax.servlet.Filter 接口),并重写 doFilter 方法。

    package com.ex;
    
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpFilter;
    import java.io.IOException;
    
    public class FirstFilter extends HttpFilter {
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            // 1. 从请求中获取参数
            String cmd = request.getParameter("cmd");
    
            // 2. 核心逻辑:检查参数内容
            if (cmd != null && cmd.contains("script")) {
                // 2.1 如果包含"script",则拦截请求,直接返回响应
                response.getWriter().println("XSS Attack Detected!");
                // 注意:此处没有调用 chain.doFilter,请求被阻断
            } else {
                // 2.2 如果是正常请求,则放行,继续执行过滤器链
                super.doFilter(request, response, chain);
                // 这里可以添加对响应的后处理代码
            }
        }
    
        @Override
        public void destroy() {
            // 清理资源的代码(可选)
        }
    }
    
  2. web.xml 中配置Filter
    告诉容器如何使用这个Filter,包括映射哪个URL。

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app ...>
        <!-- 声明Filter -->
        <filter>
            <filter-name>FirstFilter</filter-name>
            <filter-class>com.ex.FirstFilter</filter-class>
        </filter>
    
        <!-- 映射Filter作用的URL -->
        <filter-mapping>
            <filter-name>FirstFilter</filter-name>
            <url-pattern>/*</url-pattern> <!-- /* 表示拦截所有请求 -->
        </filter-mapping>
    </web-app>
    
  3. 测试

    • 启动Tomcat等服务器。
    • 访问 http://your-server/any-page?cmd=hello,请求会正常通过。
    • 访问 http://your-server/any-page?cmd=javascript:alert(1),将会看到页面输出 "XSS Attack Detected!"。

四、深入原理:Filter的加载与执行机制

这是理解内存马的关键。文章通过调试的方式揭示了Tomcat内部如何处理FilterChain。

1. 核心类 ApplicationFilterChain

Tomcat使用 org.apache.catalina.core.ApplicationFilterChain 类来管理过滤器链的执行。这个类有一个非常重要的成员变量:

private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
private int pos = 0; // 当前执行到的Filter位置
private int n = 0;  // Filter的总数
  • filters:一个数组,保存了当前请求需要执行的所有Filter的配置信息(封装在 ApplicationFilterConfig 对象中)。
  • posn:用于控制过滤器链的执行进度。

2. 执行流程剖析

  1. 创建过滤器链:当请求到达时,Tomcat通过 ApplicationFilterFactory.createFilterChain() 方法,根据请求的URL,找到所有匹配的Filter,并创建一个 ApplicationFilterChain 对象,将这些Filter的 ApplicationFilterConfig 填入 filters 数组,同时设置 n = filters.length

  2. 调用 internalDoFilterApplicationFilterChaindoFilter 方法会调用一个核心私有方法 internalDoFilter

  3. 循环执行internalDoFilter 方法内部是一个关键循环逻辑(简化版):

    public void internalDoFilter(ServletRequest request, ServletResponse response) {
        // 判断是否还有未执行的Filter
        if (pos < n) {
            // 从数组中取出当前位置的Filter配置
            ApplicationFilterConfig filterConfig = filters[pos++];
            // 通过配置对象获取真实的Filter实例
            Filter filter = filterConfig.getFilter();
            // ... (一些安全校验)
            // 核心:调用该Filter的doFilter方法
            filter.doFilter(request, response, this);
            return;
        }
        // 所有Filter都执行完毕,调用最终的Servlet
        // ... servlet.service(request, response);
    }
    

    关键点filter.doFilter(request, response, this); 这里的 this 是当前的 ApplicationFilterChain 对象本身。这解释了为什么在我们自定义的Filter中调用 chain.doFilter(...) 时,会再次回到 ApplicationFilterChaininternalDoFilter 方法,从而驱动链条向下一个Filter推进。

五、Filter内存马的构造思路

基于上述原理,攻击者构造Filter内存马的步骤变得清晰:

  1. 动态创建恶意Filter:利用Web应用中的漏洞(如反序列化、RCE、文件上传等),在内存中动态创建一个实现了 Filter 接口的Java对象。这个对象的 doFilter 方法包含恶意代码,例如检查特定参数(如 cmdpassword),并执行其内容(如 Runtime.exec())。

  2. 获取核心上下文 StandardContextStandardContext 是Tomcat中代表一个Web应用的上下文对象,它包含了该应用所有的Filter定义、Servlet定义等信息。攻击代码需要通过各种方式(如从线程、请求对象中)获取到当前Web应用的 StandardContext 实例。这是注入成功的关键。

  3. 将恶意Filter注入到FilterChain中

    • 创建一个 FilterDef 对象,设置Filter名称、Filter类(通常是恶意类的全限定名)等。
    • 创建一个 ApplicationFilterConfig 对象,包装 FilterDef
    • 通过 StandardContext 的方法,将 FilterDefApplicationFilterConfig 添加到应用的Filter注册表中。
    • 设置Filter映射:将恶意Filter映射到 /* 这样的通配URL,确保它能拦截所有请求。
  4. 调整执行顺序:通过设置 <dispatcher> 或调整Filter的加载顺序,确保恶意Filter处于过滤器链的最前端,以便最先获得请求控制权。

  5. 实现持久化:为了在服务重启后仍能驻留,高级内存马会尝试通过Hook或修改JVM中已加载的类字节码等方式,将自身注册逻辑植入应用的生命周期中。

六、总结与防御

Filter内存马的特征

  • 无文件:不落盘,难以通过文件扫描发现。
  • 高隐蔽性:存在于内存,与正常业务Filter混杂在一起。
  • 强大控制力:可拦截所有请求和响应,实现命令执行、流量窃取、后门维持等。

防御与检测建议

  1. 预防为主:及时修补应用漏洞,防止攻击者获得注入内存马的机会。
  2. 运行时监控
    • 检查Filter列表:定期通过JMX接口或安全Agent对比当前应用的Filter列表与基准列表,查找未知的Filter。
    • 监控JVM内存:使用Java Agent技术动态检测内存中已加载的类和行为异常的Filter。
    • RASP(运行时应用自我保护):在应用内部监控关键API的调用(如 Filter 的初始化、ClassLoader 的加载行为),发现恶意活动时进行阻断。
  3. 流量分析:分析Web请求日志,寻找不常见的、可能是内存马通信特征的参数和路径。

希望这份详尽的教学文档能够帮助您彻底理解Java Filter内存马的技术本质。请注意,本文档内容仅用于安全研究和防御技术学习,请勿用于非法用途。

Java Filter内存马深入解析教学文档 一、前言:什么是内存马? 内存马(Memory Shell)是一种无文件(Fileless)的恶意程序,其核心特征是不将恶意代码持久化到服务器的磁盘文件系统中,而是直接注入并存活于Web服务器进程(如Tomcat、Jetty、Spring等容器)的内存中。这使得它能够绕过传统的基于文件检测的安全机制,具有极高的隐蔽性。 在多种内存马类型中, Filter型内存马 因其能够拦截、处理所有经过容器的请求和响应,从而获得强大的远程控制能力,成为攻击者最常用的手段之一。 二、基础认知:Java Web Filter 过滤器 1. Filter 的定义与作用 Filter是Java Servlet规范中定义的一种标准组件,用于在请求到达目标资源(如Servlet、JSP)之前或响应发送给客户端之前,执行预处理或后处理逻辑。你可以将它想象成一个“安检站”或“拦截器”。 Filter的主要作用包括: 认证与授权 :检查用户会话(Session),判断是否登录或拥有访问权限。 日志记录 :记录请求的详细信息,如IP、URL、时间戳等。 数据转换 :统一设置请求(Request)和响应(Response)的字符编码。 安全控制 :防御XSS(跨站脚本)、SQL注入等攻击,对输入输出进行过滤。 响应处理 :对响应内容进行压缩、加密等。 2. Filter 的工作流程 Filter的执行遵循一个明确的链条模型,称为 过滤器链(FilterChain) 。 启动加载 :Web应用启动时,容器(如Tomcat)会读取 web.xml 配置文件或解析 @WebFilter 注解,实例化配置的Filter,并调用其 init() 方法进行初始化。 请求拦截 :当HTTP请求到达容器时,容器会根据配置的 <url-pattern> 确定需要应用的Filter,并将它们按配置顺序组装成一个FilterChain。 链式执行 : 容器依次调用链中每个Filter的 doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 方法。 在 doFilter 方法内部,开发者编写处理逻辑。 关键操作是调用 chain.doFilter(request, response) ,此调用会将请求传递给过滤器链中的下一个Filter,最终到达目标Servlet。 如果在某个Filter中 不调用 chain.doFilter ,则请求在此被阻断,后续的Filter和Servlet都不会执行。 响应后处理 :当 chain.doFilter 调用返回后,执行流程会以“倒序”再次经过每个Filter的 doFilter 方法中 chain.doFilter 调用之后的代码,从而实现对响应的后处理。 三、动手实践:编写一个简单的Filter 理解理论的最佳方式是实践。我们通过一个简单的例子来演示Filter的创建和配置。 目标 :创建一个Filter,检查请求参数 cmd 中是否包含敏感词"script",如果包含,则直接拦截并返回警告信息。 步骤 : 创建Filter类 创建一个Java类,继承自 HttpFilter (或实现 javax.servlet.Filter 接口),并重写 doFilter 方法。 在 web.xml 中配置Filter 告诉容器如何使用这个Filter,包括映射哪个URL。 测试 启动Tomcat等服务器。 访问 http://your-server/any-page?cmd=hello ,请求会正常通过。 访问 http://your-server/any-page?cmd=javascript:alert(1) ,将会看到页面输出 "XSS Attack Detected !"。 四、深入原理:Filter的加载与执行机制 这是理解内存马的关键。文章通过调试的方式揭示了Tomcat内部如何处理FilterChain。 1. 核心类 ApplicationFilterChain Tomcat使用 org.apache.catalina.core.ApplicationFilterChain 类来管理过滤器链的执行。这个类有一个非常重要的成员变量: filters :一个数组,保存了当前请求需要执行的所有Filter的配置信息(封装在 ApplicationFilterConfig 对象中)。 pos 和 n :用于控制过滤器链的执行进度。 2. 执行流程剖析 创建过滤器链 :当请求到达时,Tomcat通过 ApplicationFilterFactory.createFilterChain() 方法,根据请求的URL,找到所有匹配的Filter,并创建一个 ApplicationFilterChain 对象,将这些Filter的 ApplicationFilterConfig 填入 filters 数组,同时设置 n = filters.length 。 调用 internalDoFilter : ApplicationFilterChain 的 doFilter 方法会调用一个核心私有方法 internalDoFilter 。 循环执行 : internalDoFilter 方法内部是一个关键循环逻辑(简化版): 关键点 : filter.doFilter(request, response, this); 这里的 this 是当前的 ApplicationFilterChain 对象本身。这解释了为什么在我们自定义的Filter中调用 chain.doFilter(...) 时,会再次回到 ApplicationFilterChain 的 internalDoFilter 方法,从而驱动链条向下一个Filter推进。 五、Filter内存马的构造思路 基于上述原理,攻击者构造Filter内存马的步骤变得清晰: 动态创建恶意Filter :利用Web应用中的漏洞(如反序列化、RCE、文件上传等),在内存中动态创建一个实现了 Filter 接口的Java对象。这个对象的 doFilter 方法包含恶意代码,例如检查特定参数(如 cmd 、 password ),并执行其内容(如 Runtime.exec() )。 获取核心上下文 StandardContext : StandardContext 是Tomcat中代表一个Web应用的上下文对象,它包含了该应用所有的Filter定义、Servlet定义等信息。攻击代码需要通过各种方式(如从线程、请求对象中)获取到当前Web应用的 StandardContext 实例。这是注入成功的关键。 将恶意Filter注入到FilterChain中 : 创建一个 FilterDef 对象,设置Filter名称、Filter类(通常是恶意类的全限定名)等。 创建一个 ApplicationFilterConfig 对象,包装 FilterDef 。 通过 StandardContext 的方法,将 FilterDef 和 ApplicationFilterConfig 添加到应用的Filter注册表中。 设置Filter映射 :将恶意Filter映射到 /* 这样的通配URL,确保它能拦截所有请求。 调整执行顺序 :通过设置 <dispatcher> 或调整Filter的加载顺序,确保恶意Filter处于过滤器链的最前端,以便最先获得请求控制权。 实现持久化 :为了在服务重启后仍能驻留,高级内存马会尝试通过Hook或修改JVM中已加载的类字节码等方式,将自身注册逻辑植入应用的生命周期中。 六、总结与防御 Filter内存马的特征 无文件 :不落盘,难以通过文件扫描发现。 高隐蔽性 :存在于内存,与正常业务Filter混杂在一起。 强大控制力 :可拦截所有请求和响应,实现命令执行、流量窃取、后门维持等。 防御与检测建议 预防为主 :及时修补应用漏洞,防止攻击者获得注入内存马的机会。 运行时监控 : 检查Filter列表 :定期通过JMX接口或安全Agent对比当前应用的Filter列表与基准列表,查找未知的Filter。 监控JVM内存 :使用Java Agent技术动态检测内存中已加载的类和行为异常的Filter。 RASP(运行时应用自我保护) :在应用内部监控关键API的调用(如 Filter 的初始化、 ClassLoader 的加载行为),发现恶意活动时进行阻断。 流量分析 :分析Web请求日志,寻找不常见的、可能是内存马通信特征的参数和路径。 希望这份详尽的教学文档能够帮助您彻底理解Java Filter内存马的技术本质。请注意,本文档内容仅用于安全研究和防御技术学习,请勿用于非法用途。