Spring内存马新手实战指南- Controller篇
文章介绍了Spring框架及其模块Spring Boot的基本概念,探讨了Spring内存马的工作原理及其检测方法,并详细分析了Controller的概念与实现机制。通过创建一个简单的Spring Boot项目并进行调试,展示了Controller的注册与请求处理流程。 2025-10-23 09:55:23 Author: www.freebuf.com(查看原文) 阅读量:1 收藏

基础概念

Spring:是 Java 生态中最流行的应用程序框架,它是使用 Java 语言编写的一个开源企业应用开发框架,提供全面的编程和配置模型。

Spring Boot:是Spring框架的一个模块,旨在简化Spring应用程序的初始搭建和开发过程。它提供了默认配置,可以快速创建独立的、生产级的基于Spring的应用程序,其内置了Tomcat,使用Spring Boot时,我们通常不需要关心Servlet容器的配置,因为它提供了自动配置。而直接使用Tomcat时,我们需要手动配置和部署文件。

Spring内存马:是一种无文件、驻留在内存中的恶意后门程序,它利用Spring框架的机制动态注册恶意组件,从而实现对受入侵服务器的持久化控制。由于其完全存在于内存中,不写入磁盘,Spring内存马的隐蔽性很高,因为它利用的是Spring正常的机制,而且没有文件落地。检测Spring内存马需要监控Spring容器的动态变化,例如检查新注册的Controller、Interceptor或Bean,以及监控不正常的URL映射。

Spring内存马工作原理

Spring内存马通常通过利用应用程序的漏洞(如反序列化漏洞、SQL注入、文件上传漏洞等)注入,然后动态注册恶意的Spring组件(如Controller、Interceptor等)。这些组件与正常业务组件一样运行在Spring容器中,但包含恶意功能。

常见的Spring内存马类型包括:

Controller内存马:攻击者动态注册一个恶意的Controller,该Controller可以映射特定的URL路径,当访问该路径时,执行攻击者指定的命令(如系统命令、文件操作等)并返回结果。

Interceptor内存马:攻击者注册一个自定义的拦截器(Interceptor),该拦截器会在每个请求处理之前或之后执行恶意代码。由于拦截器可以拦截多个请求,因此这种内存马具有较高的触发频率。

Bean内存马:通过修改Spring容器中的已有Bean或动态注册新的Bean,在Bean的初始化方法或业务方法中植入恶意代码。这种内存马可能利用Spring的依赖注入机制,在Bean被其他组件使用时触发。

RequestMapping内存马:通过动态修改RequestMapping映射,将恶意方法绑定到特定的URL上,当访问该URL时触发恶意代码。

什么是Controller-概念

Spring的Controller作为Web请求的入口点,我们通常会在Controller中定义处理HTTP请求的方法,这些方法可以返回数据(如JSON)或视图(如HTML页面)。 在Spring中Controller是Spring MVC框架中的核心组件,负责接收用户请求(接收HTTP请求(GET、POST、PUT、DELETE等))、处理业务逻辑(调用Service层处理业务逻辑)、返回响应结果(返回客户端内容)。它是Web应用的"调度中心",协调用户界面与后端服务之间的交互。Spring MVC中的Controller核心组件,包括Handler、Mapping、Method、HandlerMapping等。

Handler(处理器):

Handler是处理请求的组件。它可以是任何类型的对象,但通常是我们编写的Controller类中的方法(即被@RequestMapping或其变体注解的方法)。Handler负责处理具体的请求,并返回模型和视图或直接返回数据。

Mapping(映射):

映射指的是将HTTP请求(URL、方法、参数等条件)与特定的Handler关联起来的过程。可以通过注解(如@RequestMapping)或配置来定义映射。

Method(方法):

这里特指Controller类中处理请求的具体方法。一个Controller类中可以有多个方法,每个方法都可以是一个独立的Handler,处理不同的请求。

HandlerMapping(处理器-映射关系):

它的职责是根据请求(HttpServletRequest)找到对应的Handler(即处理请求的控制器方法)。可registerMapping() 方法动态注册映射关系

核心特性

1. 注解驱动:使用注解声明Controller和请求映射,减少XML配置,提高开发效率

2. 参数绑定:自动将请求参数绑定到方法参数,支持路径变量、查询参数、请求体等

3. 内容协商:根据Accept头返回不同格式数据,支持JSON、XML、HTML等多种媒体类型

4. 异常处理:统一的异常处理机制,可自定义错误响应格式

巧动手

现在开始创建一个spring boot的项目,认识Controller:直接使用脚手架!

首先到下图的站点直接下载demo,自动创建的项目,下载之后在idea中打开即可:

1761200467_68f9c95390017cbd69096.png!small?1761200468480

打开之后项目结构是这样的:

1761200505_68f9c9797260c567936bb.png!small?1761200505988

在项目下新建一个自己的controller:FirstController.java,代码如下:遵循Spring规范!

package com.example.demo.demos.web;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")  // 添加基础路径
public class FirstController {
    @GetMapping("/index")  // 通过@GetMapping注解,添加url访问路径
public String index() {
        return "index page";
    }
    // 可通过注解可以添加更多端点---url访问路径
@GetMapping("/hello")
    public String hello() {
        return "Hello Spring Boot!";
    }
}

直接主函数启动项目:1761200583_68f9c9c71bae9e54c2531.png!small?1761200583566

访问http://127.0.0.1:8080/api/index页面正常访问:完成了一个controller的创建,执行!

1761200599_68f9c9d738bb1141b428f.png!small?1761200599572

小结:通过这里其实可以看出来,这跟java的servlet的配置很像,只是这里通过spring注解(@RequestMapping主路径配置,@GetMapping()主路径下的路径与方法绑定)的方式自动实现了,url访问路径与类的绑定关系。而且spring内置了tomcat启动web项目。

那么有没有可能思路是差不多的,只需要弄清楚这个绑定关系是如何实现的就可以了!现在来验证一下这个推断:

controller加载过程分析

现在通过调试的方式尝试找controller的请求过程中加载过程:

在启动应用的时候,spring就会将所有的已有的controller分别逐一注册!并形成对应的映射关系,类似tomcat的servlet读取xml文件一样。

1761200831_68f9cabf3fee7197d8a6e.png!small?1761200831724

现在通过调试的方式尝试找controller的请求过程中加载过程:

在firstController的index处test()方法打上断点:如下图所示,在执行我们的test的方法之前,通过反射执行了很多方法。通过反射执行可能与我们关注的没有太大关系。毕竟上面我们得到的结论可能跟servlet类似。

1761201285_68f9cc85e09ee8110fdb5.png!small?1761201286494

直接来到下面的划线部分,果然看到了HttpServlet,且执行了对应的doGet方法,与servlet内存马分析的时候几乎一样!可能重点就在这附近。

1761201315_68f9cca39f39fcf6dd1e0.png!small?1761201316052

并且该方法的参数中的request的attributes属性值中确实包含了我们的请求api/index。

1761201371_68f9ccdb2b0194e25b925.png!small?1761201371704

如下图结构:req 是一个RequestFacade对象,持有一个request对象,request对象中的属性attributes是一个hashMap,里面有我们的请求相关的信息。

1761201554_68f9cd9213e5e9eb8b21f.png!small?17612015548321761201571_68f9cda386206a4cf575d.png!small?1761201572111

将其作为参数,执行了doService(request, response);方法。

关键赋值:再这个方法里面对request请求进行了一系列赋值操作,将其中一个key值,设置了一个webApplicationContext对象:

1761201680_68f9ce109369ffb57cfcc.png!small?1761201681165

然后再次传递给doDispatch(request, response);方法

1761201753_68f9ce591e7b90ae6b7ab.png!small?1761201753656

该方法就对request做了很多操作了:先把他赋值给一个HttpServletRequest processedRequest = request; 变量。

1761201774_68f9ce6e7783a745b26be.png!small?1761201775237

然后从里面拿到了我们的请求信息api/index:1761201794_68f9ce8204fce050cf70e.png!small?1761201794545

然后下一行得到一个mappedHandler,这个对象竟然是我们的FirstController的test()方法:

1761201815_68f9ce973c39c28a4debf.png!small?1761201815704

并且该方法后面调用了doDispatch,将processedRequest,response,mappedHandler的handler属性一起作为参数执行。再往后就是一些反射调用,执行到我们的test()方法了。该方法前面还有注释

// Actually invoke the handler.// 实际调用处理器。

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

),实际调用处理器(即我们写的Controller方法)处理请求。

那么大概率doDispatch()方法里面,就对request请求进行了一些列操作,然后得到了对应得需要执行得方法。

回到下图中代码处mappedHandler得取值方法:这个方法前面也有个注(释// Determine handler for the current request.,确定当前请求的处理器),可能这个地方就是我们想要的答案!1761201888_68f9cee0059459937afb5.png!small

跟进这个方法:该方法第一行的handlerMappings里面存放了所有URL映射关系的核心配置

1761201999_68f9cf4f2b683d2ce317d.png!small?1761201999662

在它的属性mappingRegistry得registry中如下图:-这正是我们想要找的映射关系!

1761202037_68f9cf751b577884ae40f.png!small?1761202038013

这个handlerMappings就可能是保存了服务器原始controller映射关系的对象。

1761202054_68f9cf863e6634c21e50d.png!small?1761202055333

request请求-映射匹配过程:

直接详细分析源代码看看具体如何做得:

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
       for (HandlerMapping mapping : this.handlerMappings) {
       

文章来源: https://www.freebuf.com/articles/web/453853.html
如有侵权请联系:admin#unsafe.sh