【干货】Servlet 内存马加载流程分析
前言
Servlet 内存马的最后一篇,和之前的分析其实差不太多,基础知识就不进行了,这篇文章就直接从加载开始说起,废话不多说直接开始吧。
一、Servlet 流程分析
(一)StrandContext.startInternal 方法
首先我们需要先了解一下 Tomcat 的加载流程,这里借用师傅的一张图。
我们通过上图 Tomcat 的启动流程可以得知,首先我们启动到 StandardHost 就会去启动 StrandContext,我们和之前内存马的分析一样,我们从 Contextconfig 类加载配置文件开始分析,我们打好断点,然后开始查看栈信息。
跟进上图的栈信息,我们可以看到 StandardHost 之后就会走到 StrandContext,我们跟到 StrandContext 进行查看。可以看到我们此时执行的是 StandardContext. startInternal(),根据栈的信息,我们在 this.fireLifecycleEvent 位置正在解析 web.xml 并启动 StandardWrapper。我们继续往下看代码。
startInternal()方法在最后依次调用了 listenerStart、filterStart、loadOnStartup 方法,我们跟到 loadOnStartup 方法进行查看。
这里我们首先对 children 进行了一个变量,大概猜测 children 应该是存储 servlet 的变量。接下来做了一个迭代,执行了 wrapper.load()对其进行加载。我们这里其实就是我们加载 wrapper 的地方。
【一>所有资源获取<一】1、网络安全学习路线 2、电子书籍(白帽子)3、安全大厂内部视频 4、100 份 src 文档 5、常见安全面试题 6、ctf 大赛经典题目解析 7、全套工具包 8、应急响应笔记
(二)ContextConfig 读取配置文件
到这里我们已经跟到了 StandardContext 加载 Wrapper 的位置,最关键的参数其实就是 children,所以我们回到刚才 ContextConfig 的位置继续进行分析。首先 ContextConfig 读取 web.xml 配置文件,我们跟到 configureContext()方法查看代码,首先读取到 servlet 后进行迭代。
接下来执行 createWrapper 方法创建了一个 wrapper。继续向下
接下来设置了一个 ServletName 和 ServletClass(这里其实对应的就是 web.xml 中的 servlet),继续向下
(三)addChild 启动 wrapper 线程
读取到 Servlet 配置之后,执行了 addChild(wrapper)方法,我们跟进 addChild()方法进行查看。
我们跳到了 StandardContext.addChild(),其实 servlet 的添加就是在这里面进行的。我们继续跟进去简单看一下吧。
我们走到了 ContainerBase.addChild(),这个类其实是一个骨架类,这里我们需要注意两个位置,一个是我们将 wrapper 添加到了 ContainerBase 的 children 变量中,另一个是我们最后执行到了 child.start(),我们继续向下查看。
我们跳到了 LifecycleBase,这个类主要是和 Tomcat 的生命周期有关,我们跟到 this. startInternal()进行查看。
此时我们通过 LifecycleBase 走到了 StandardWrapper.startInternal(),查看该方法其实这里本质就是调用父类的 startInternal() 线程池启动。我们点击去稍微看一下 super.startInternal()进行查看
我们又走到了 ContainerBase.startInternal(),方法最后启动了线程。
我们执行完 addChild()方法后,我们回到了 ContextConfig 类,继续向下查看,可以看到此时执行了 webxml.getServletMappings().entrySet().iterator(),其实这里的本质是就是读取 servler-mapping。
我们跟到 addServletMappingDecoded()方法,跳到了 StandardContext. addServletMappingDecoded()方法下,首先将 servlet-name 和 url-pattern 添加到 servletMappings 下,然后通过 findChild 方法,找到我们之前启动的 wrapper(读取之前存储到 ContainerBase 的 children 变量),我们跟进查看 wrapper.addMapping()方法。
我们走到了 StandardWrapper.addMapping 方法。将 mapping(也就是 url-pattern)添加到 mappings 变量下。
二、Servlet 内存马实现
其实看到这里应该能明白添加 servlet 最关键的地方就是这个两个方法,所以我们接下来内存马的编写就需要一个恶意的 servlet,然后通过 addChind 和 addServletMappingDecoded 将其恶意 servlet 注入进去。
最终成功实现内存马。
结尾~~~
文章里面有什么写的不对的地方,望师傅们多加指正。Servlet 型内存马算是写完了,个人觉得 Servlet 内存马是这三个里面最难的,因为调试的时候栈里面来回的绕,有时候跳着跳着就乱了,文章里面可能有些地方有写的不对的地方,希望师傅们多加指正。
评论