Tomcat下基于Listener的内存Webshell分析

前言

在浏览《JSP Webshell那些事 -- 攻击篇(下)》时里面提到了一种不常见的内存马构造方式,一般来说Tomcat下的内存马都是基于StandardContext来构建的,并且文中提到了web.xml对于这三种组件的加载顺序是:listener -> filter -> servlet,也就是说listener的优先级为三者中最高的。笔者也是第一次见到这种内存马,并且作者提到了该内存马的构造方式更为简单,因此本着研究的态度来分析下该内存马的构造流程。

Listener监听器介绍

监听器Listener就是在application,session,request三个对象创建、销毁或者往其中添加修改删除属性时自动执行代码的功能组件。

Listener是Servlet的监听器,可以监听客户端的请求,服务端的操作等。

其中Listener的监听主要分为三类

1.ServletContext监听:用于对Servlet整个上下文进行监听(创建、销毁)

2.Session监听:对Session的整体状态的监听

3.Request监听:用于对Request请求进行监听(创建、销毁)

对于这三类,熟悉java和Tomcat的同学应该也知道,对于request的请求和篡改是常见的利用方式,这里选取ServletRequestListener作为研究重点,重点看看其中是否存在可以添加Listener的构造函数,将我们的webshell功能写进Listener里去。

Listener型内存马分析

首先ServletRequestListener作为接口,我们可以重定义里面的逻辑,写进我们执行命令的语句,接着我们开始寻找Tomcat环境中能够加入Listener的构造方法。

在ApplicationContext类中存在addListener构造方法,那么到这里思路就已经很清晰了,通过获取当前Context对象,进而反射获取ApplicationContext对象,然后通过addListener函数调用我们构造的恶意Listener,实现内存Webshell。

但是在这里如果想到通过ApplicationContext类来加入我们Listener会进入到上述代码,其中if语句会判断context当前所属的Tomcat生命周期是否正确,否则抛出异常,也就是说通过这里的addListener其实并不会添加成功,仔细查看代码,最终添加Listener的代码是这行

通过debug会发现这里ApplicationContext的context对象为StandardContext,进而调用StandardContext的addApplicationEventListener函数

这里并没有任何context的判断,可以直接添加listener,因此在获取到ApplicationContext对象后,继续通过反射获取StandardContext对象,最终调用addApplicationEventListener函数。

Listener型内存马构造

在实际场景中将该文件作为shell.jsp上传至服务器,然后进行访问,因为没有out输出,因此页面会显示空白,正常情况下此时内存马已经生成了

接下来访问任意路径,并传入cmd参数,可以看到此时基于Listener的内存马已经生效了。