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都不会执行。
- 容器依次调用链中每个Filter的
- 响应后处理:当
chain.doFilter调用返回后,执行流程会以“倒序”再次经过每个Filter的doFilter方法中chain.doFilter调用之后的代码,从而实现对响应的后处理。
三、动手实践:编写一个简单的Filter
理解理论的最佳方式是实践。我们通过一个简单的例子来演示Filter的创建和配置。
目标:创建一个Filter,检查请求参数cmd中是否包含敏感词"script",如果包含,则直接拦截并返回警告信息。
步骤:
-
创建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() { // 清理资源的代码(可选) } } -
在
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> -
测试
- 启动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对象中)。pos和n:用于控制过滤器链的执行进度。
2. 执行流程剖析
-
创建过滤器链:当请求到达时,Tomcat通过
ApplicationFilterFactory.createFilterChain()方法,根据请求的URL,找到所有匹配的Filter,并创建一个ApplicationFilterChain对象,将这些Filter的ApplicationFilterConfig填入filters数组,同时设置n = filters.length。 -
调用
internalDoFilter:ApplicationFilterChain的doFilter方法会调用一个核心私有方法internalDoFilter。 -
循环执行:
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(...)时,会再次回到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内存马的技术本质。请注意,本文档内容仅用于安全研究和防御技术学习,请勿用于非法用途。