Listener型内存马不像Filter型那么复杂。Listener型内存马只涉及一个变量applicationEventListenersList。
tomcat有一堆listener,其中ServletRequestListener是监听http请求的,最适合做内存马。
准备环境
和Filter型内存马一样新建项目,然后写一个正常的listener。
1 | package org.example.demo3; |
Java内存马系列-04-Tomcat 之 Listener 型内存马 | Drunkbaby’s Blog这里面讲错了,@WebListener
注解里的参数是注解的描述,所以@WebListener("/listenerTest")
只能把类注册为监听器。没有指定路径的作用,即使加上这个注解,listener还是对所有路径都生效。
web.xml
1 |
|
pom.xml里加上
1 | <dependency> |
Listener调用过程分析
在自己写的listener里的 requestInitialized里打断点,调试运行
往上找一层,发现是从org.apache.catalina.core.StandardContext#fireRequestInitEvent过来的
1 | public boolean fireRequestInitEvent(ServletRequest request) { |
往上找,listener变量是instance转的,instance是从instances里取的,instances是getApplicationEventListeners()
返回的,进到这个函数里看看:
1 | public Object[] getApplicationEventListeners() { |
显然和之前的filterConfigs、filterMaps、filterDefs一样,applicationEventListenersList也是context下的一个存储信息的变量,只不过存储的是listener的信息。
搜索applicationEventListenersList,直接找到了addApplicationEventListener方法。反射拿到StandardContext之后直接调用这个方法添加一个恶意的listener就行了。
构造内存马
先写用正常的写法,写一个带webshell的listener。此时会遇到一个问题,在listener里访问不到response,也就没办法回显命令执行的结果。
sre.getServletRequest()
实际返回的是一个RequestFacade,里面是有一个Request类型的request的,而Request类的getResponse()
可以获取到response。
1 | package org.example.demo3; |
把这个恶意listener用addApplicationEventListener
添加到StandardContext里就行了。StandardContext还是用反射一步一步获取:
1 | <%@ page import="java.lang.reflect.Field" %> |
访问这个jsp之后,访问任意路径带上cmd参数即可执行命令。