ServletListenerFilter内存马查杀手段一
2023-6-18 20:7:44 Author: www.freebuf.com(查看原文) 阅读量:6 收藏

Preface

之前的文章基本上都是围绕在tomcat或者spring环境下如何解决下面几个问题

  1. 如何获取reqest / response域,也即是如何回显

  2. 如何利用这些中间件的特性进行内存马的构造,也即是动态创建路由

  3. 如何在反序列化漏洞的过程中注入内存马进行不出网等等的利用

总的来说,主要是从“攻”的角度进行内存马的学习

接下来在继续抽空学习和挖掘其他的内存马的过程中,也开始从“防”的角度学习内存马的查杀

前面几节主要是通过已知的一些查杀工具学习学习别人的思路

Memshell-scanner

第一个学习的项目是c0ny1师傅在几年前开源的项目了

https://github.com/c0ny1/java-memshell-scanner

这个项目的主要思路是获取Tomcat下的所有的存在的Servlet/Filter/Listener

分别获取对应的各种信息,主要是通过判断对应的Servlet / Filter / Listener类是否在服务器端存在有对应的class文件,如果在classpath下没有对应的类文件则判断其可能为内存马

在这个tool中,内置了两个功能

  1. dump下对应的class文件

  2. kill掉对应的servlet / fitler / listener

接下来我们详细看看,是怎么进行判断的

Detection

Servlet

首先看看两个方法

getChildren

image-20230614225130255.png

这里方法的作用主要是通过从request这个HttpServletRequest对象中获取StandardContext类对象,这个类是"上下文接口的标准实现。每个子容器都必须是一个 Wrapper 实现,以处理指向特定 servlet 的请求。", 之后获取这个对象中的children属性,这个属性也是其父类ContainerBase的值

image-20230614225623564.png

也就是基于这个Context的一些子容器

也就是一些servlet -> StandardWrapper的映射

image-20230614225815948.png

而另一个方法getServletMaps

image-20230614225902081.png

类似的是从StandardContext对象中获取其属性servletMappings,也即是一些router -> servletName的映射

image-20230614230103608.png

之后的主要步骤也即是

image-20230614230628760.png

通过遍历存在的所有的所有路由

分别获取servletPath / servletName / standardWrapper这些信息之后

  1. 通过Class.forName进行类的加载

  2. 获取对应servlet类的classloader

  3. 通过对应的classloader调用getResource方法获取源文件

如果没有获取到文件就认为其可能为内存马

consideration

结合前面Tomcat Servlet的动态注册一个servlet的过程

Servlet存马的创建流程

  • 创建恶意Servlet

  • 用Wrapper对其进行封装

  • 添加封装后的恶意Wrapper到StandardContext的children当中

  • 添加ServletMapping将访问的URL和Servlet进行绑定

  1. 将恶意Servlet的Wrapper添加进入了children属性中

  2. 将路由的映射添加进入了servletMappings属性中

这里的检测思路就是在我们注册内存马的必要步骤中进行处理

Filter

针对于Filter型内存马的检测,采取的是和Servlet型类似的检测方法,也即是获取所有存在的Filter,之后判断资源文件是否存在

类似的,简单看一下两个关键的方法

getFilterConfig

image-20230615211313393.png

这个方法同样是获取了StandardContext对象之后获取其中的filterConfigs属性值

这个属性值存放的也就是每一个filter的filterName -> applicationFilterConfig的一个映射,其中他的value值ApplicationFilterConfig对象中存放的是一些关于过滤器的配置内容,比如filterName / filterClass等等信息,在前面进行filter型内存马的编写的时候也涉及到了这个类对象的创建

image-20230615211539767.png

第二个方法是getFilterMaps

image-20230615211954683.png

类似的是获取的是StandardContext类对象中的filterMaps属性

image-20230615212230073.png

对于该属性的描述

此应用程序的过滤器映射集,按照它们在部署描述符中定义的顺序,以及通过 ServletContext 添加的额外映射,可能在部署描述符中定义的映射之前和之后。

在获取了和filter有关的信息之后,检测的主体是:

image-20230615212453100.png

  1. 遍历所有的filter

  2. 分别获取每一个filterfilterName / filterClass / filterClassLoaderName等等信息

  3. 最后在结果输出中,调用了classFileIsExists方法根据filterClass来判断是否存在有对应的filterClass的文件资源,有,则输出对应的path路径,没有,则说明可能为内存马

consideration

同样的,我们可以结合filter内存马的注入流程

只需要设置filterMaps、filterConfigs、filterDefs就可以注入恶意的filter

  • filterMaps:一个HashMap对象,包含过滤器名字和URL映射

  • filterDefs:一个HashMap对象,过滤器名字和过滤器实例的映射

  • filterConfigs变量:一个ApplicationFilterConfig对象,里面存放了filterDefs

这里的检测同样是根据注入的关键点进行检测

Listener

针对tomcat下的所有的listener的获取,涉及到的方法是getListenerList

image-20230615213641068.png

同样是在StandardContext对象中获取他的applicationEventListenersList属性值

image-20230615213747280.png

这个属性值存放的一些application event listener的列表

接下来对于检测listener型内存马的主题部分是

image-20230615215826157.png

这里在获取了所有的listener之后,在其中筛选出了ServletRequestListener的实现类作为检测的目标

至于什么是ServletRequestListener? 以及除了这类listener还有什么其他类型的监听器?

这些答案在前面进行listener内存马的构造一文中能够找到

因为这里仅仅是筛选了ServletRequestListener的实现类进行检测,虽然常见的Listener动态创建的监听器是这个实现类,但是如果选择其他类型的监听器进行内存马的注入,这是,如果采用这种方法将会出现检测不全的情况

image-20230615220233016.png

同样会对listener类进行是否存在类资源进行一定的检查

consideration

结合之前的listener型内存马的注入方式

内存马编写流程

  1. 首先获取到StardardContext对象

  2. 之后创建一个实现了ServletRequestListener 接口的监听器类

  3. 再然后通过调用StardardContext类的addApplicationEventListener方法进行Listener的添加

这里同样是通过对应的流程进行检测

Others

这里除了存在有是否是内存马的判断功能,还存在有servlet / filter / listener类的下载功能以及查杀功能

dump

image-20230615220707748.png

直接就是在通过Repository.lookupClass方法获取指定的类之后将其内存写入response body中返回

kill

这里分别采用不同的方式进行kill

针对servlet型是在deleteServlet方法中

image-20230615221359804.png

在简单的根据传入的HttpServletRequest / ServletClassName获取到了urlPattern / ServletClass等信息

  1. 通过反射调用StandardContext对象的removeServletMapping方法
    image-20230615221558603.png
    这个方法是用来根据路由删除注册的servlet

  2. 通过反射调用StandardContext对象的removeChild方法
    image-20230615221912054.png
    这个方法是为了不仅仅在前面删除掉路由和servlet的映射关系,也删除掉创建的对应的Wrapper对象

而针对Filter型的内存马,主要是在deleteFilter方法中

image-20230615222134354.png

  1. 获取一些关于filter的一些信息

  2. 之后就是获取FilterDef类对象之后,反射获取他的removeFilterDef方法,传入filterDef,也就是过滤器名字和过滤器实例的映射
    image-20230615222348701.png
    进行映射的移除

  3. 之后又是获取FilterMap类对象之后,反射获取他的removeFilterMap方法,调用进行过滤器名字和URL映射的删除
    image-20230615222524037.png

总的来说,servlet和filter的删除主要是针对在构造的过程中的一些反方向,在构造过程中添加了什么,在kill的过程中也将要将添加的内容通过调用对应的api进行删除

Conclusion

通过整个对Servlet / Listener / Filter型内存马的查杀的原理分析,能够知道这种方式也就是采用直接获取所有的servlet / Listener / Filter的目标类,进而判断是否存在有文件存在来判断

针对这类型的绕过,比如前面分析到的Listener型内存马的检查,这里仅仅是筛选了其中的一种Listener进行检测,这种方式可能会导致一些漏扫之类的问题,而且这种对于是否是内存马的判断是直接判断是否能够获取到对应类的文件,这种方式并没有判断该类文件的危害性,同样存在有漏扫和误报的情况在


文章来源: https://www.freebuf.com/articles/web/369799.html
如有侵权请联系:admin#unsafe.sh