Java内存马中的Servlet,Filter,Listener
字数 2585 2025-09-01 11:26:11
Java内存马技术深度解析:Servlet、Filter与Listener的实现与防御
一、内存马核心概念
1.1 内存马的本质
内存马(Memory Shell)是一种驻留在目标Java Web应用进程中的持久化、隐蔽后门,与传统Webshell相比具有以下特性:
- "内存"特性:不依赖磁盘文件,恶意代码(类字节码)直接驻留在JVM内存中
- 实现方式:通过运行时动态生成类(BCEL/ASM)、利用应用自身功能动态注册组件或篡改现有组件
- "马"特性:伪装成或寄生在应用的合法部分(Servlet/Filter/Listener)
- 持久化:依赖目标应用生存周期,应用不重启则持续有效
1.2 内存马的工作流程
- 监听特定请求路径或事件
- 解析请求中的加密/混淆指令
- 执行恶意操作(命令执行、文件操作、内存扫描等)
- 将结果返回给攻击者
二、Servlet内存马技术
2.1 Servlet的正常角色
Java EE中处理HTTP请求的核心组件,负责:
- 接收请求
- 处理业务逻辑
- 生成响应
2.2 内存马实现原理
动态注册恶意Servlet
// 伪代码示例
ServletContext servletContext = request.getServletContext();
ServletRegistration.Dynamic registration = servletContext.addServlet(
"MaliciousServletName",
new EvilServletClass()
);
registration.addMapping("/hidden-backdoor-path");
registration.setLoadOnStartup(1);
恶意代码注入点
主要存在于以下方法中:
service()doGet()doPost()
恶意逻辑包括:
- 检查请求中的"暗号"(特定参数/Header/Cookie)
- 解析加密/混淆的指令(如Base64编码的
cmd参数) - 执行指令(通过
Runtime.exec()或ProcessBuilder) - 加密/混淆执行结果并写入HTTP响应
2.3 特点分析
- 直接性:最接近传统Webshell的实现方式
- 路径唯一性:绑定到特定URL路径,可能被扫描器发现
- 依赖容器API:需要访问
ServletContext对象
三、Filter内存马技术
3.1 Filter的正常角色
在请求到达Servlet之前和响应发送给客户端之前执行预处理和后处理,用于:
- 身份验证
- 日志记录
- 编码转换等
3.2 内存马实现原理
动态注册恶意Filter
// 伪代码示例
ServletContext servletContext = request.getServletContext();
FilterRegistration.Dynamic registration = servletContext.addFilter(
"MaliciousFilterName",
new EvilFilterClass()
);
registration.addMappingForUrlPatterns(
EnumSet.of(DispatcherType.REQUEST),
true,
"/*"
);
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
恶意代码注入点
主要存在于doFilter()方法中:
- 检查每个请求是否包含"暗号"
- 匹配则:
- 截获请求并执行恶意指令
- 加密/混淆结果并通过
HttpServletResponse输出 - 中断Filter链(不调用
chain.doFilter())
- 不匹配则正常放行请求
3.3 特点分析
- 高隐蔽性:监听所有请求路径(
/*),无需特定URL - 破坏链完整性:匹配恶意请求时中断链
- 当前主流:最常见和危害最大的内存马类型之一
四、Listener内存马技术
4.1 Listener的正常角色
监听Web应用中的各种事件,包括:
ServletContext的创建/销毁HttpSession的创建/销毁/属性变更ServletRequest的创建/销毁
4.2 主要Listener类型及利用
4.2.1 ServletRequestListener
public void requestInitialized(ServletRequestEvent sre) {
// 在每个请求开始时检查"暗号"
// 执行恶意操作但不能直接回显
// 结果存储在请求属性或ThreadLocal中
}
public void requestDestroyed(ServletRequestEvent sre) {
// 请求结束时清理痕迹
}
4.2.2 ServletContextListener
public void contextInitialized(ServletContextEvent sce) {
// Web应用启动时调用
// 可动态注册其他类型内存马
}
public void contextDestroyed(ServletContextEvent sce) {
// Web应用停止时调用
}
4.2.3 HttpSessionListener
public void sessionCreated(HttpSessionEvent se) {
// 劫持所有会话
// 植入会话木马
}
public void sessionDestroyed(HttpSessionEvent se) {
// 清理会话相关资源
}
4.3 特点分析
- 极高隐蔽性:无URL映射,仅监听事件
- 多功能性:
- 作为其他内存马的"加载器"
- 实现无文件持久化
- 非回显利用(内存扫描、线程注入等)
- 持久化关键:
contextInitialized方法可实现重启后自动重新注入
五、内存马注入技术详解
5.1 常见注入入口点
- 漏洞利用:
- RCE漏洞(反序列化、表达式注入等)
- 文件上传漏洞
- 已有Webshell
- 中间件管理接口/配置错误
5.2 通过Fastjson反序列化注入
5.2.1 Servlet注入关键步骤
-
获取
ServletContext:ServletContext servletContext = request.getSession().getServletContext(); // 通过反射获取StandardContext -
动态注册恶意Servlet:
ServletRegistration.Dynamic registration = context.addServlet( "LegitServletName", new EvilServletClass() ); registration.addMapping("/static/..;/admin/healthcheck");
5.2.2 Filter注入关键步骤
-
获取
StandardContext:// 通过线程类加载器获取 WebappClassLoaderBase classLoader = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader(); StandardContext stdCtx = (StandardContext) classLoader.getResources().getContext(); -
构造并注册恶意Filter:
// 创建FilterDef FilterDef filterDef = new FilterDef(); filterDef.setFilterName("LegitFilter"); filterDef.setFilterClass(EvilFilter.class.getName()); stdCtx.addFilterDef(filterDef); // 创建FilterMap FilterMap filterMap = new FilterMap(); filterMap.setFilterName("LegitFilter"); filterMap.addURLPattern("/*"); stdCtx.addFilterMapBefore(filterMap);
5.2.3 Listener注入关键步骤
- 反射注册恶意Listener:
// 获取applicationEventListenersList字段 Field listenersField = StandardContext.class.getDeclaredField("applicationEventListenersList"); listenersField.setAccessible(true); // 添加恶意监听器 Object[] newListeners = Arrays.copyOf(currentListeners, currentListeners.length + 1); newListeners[currentListeners.length] = listener;
5.3 高级规避技术
-
路径混淆:
registration.addMapping("/a/../%3b/bypass"); -
内存加密:
byte[] bytecode = encrypt(evilServletBytes); Class<?> clazz = new CustomClassLoader().defineClass("com.legit.Utils", bytecode); -
反检测机制:
if (System.getProperty("arthas") != null) { Thread.sleep(30000); // 延迟响应干扰扫描 }
六、检测与防御方案
6.1 检测挑战
- 无文件落地
- 代码驻留内存
- 利用合法API
- 伪装性强
6.2 检测思路
- 运行时分析:
- 扫描注册组件(Servlet/Filter/Listener)
- 检查类加载来源
- 字节码分析:
- 查找危险方法调用(
Runtime.exec、defineClass等)
- 查找危险方法调用(
- 行为监控:
- 异常网络连接
- 敏感文件访问
- HTTP请求/响应异常模式
- Agent/RASP:
- 监控类加载、方法执行、组件注册等
6.3 防御措施
-
基础防御:
- 及时修补漏洞
- 最小权限原则
- 安全配置(禁用不必要管理接口)
-
技术防护:
<!-- Tomcat禁用动态注册 --> <Context> <JarScanner scanClassPath="false" /> </Context> -
监控与响应:
- WAF/RASP部署
- 安全基线与变更监控
- 定期内存扫描
6.4 专项检测工具
-
内存扫描:
jcmd <PID> VM.classes | grep 'Filter' -
RASP检测示例:
public void addFilter(ServletContext ctx, String name, Filter filter) { if (filter.getClass().getName().contains("Evil")) { throw new SecurityException("Blocked malicious filter!"); } }
七、技术演进与趋势
7.1 最新绕过技术
- GraalVM Native Image:编译Filter代码绕过类加载监控
- Project Loom:利用虚拟线程隐藏执行流
- eBPF层隐藏:在内核层面隐藏恶意线程
7.2 防御体系构建
三维防护策略:
- 应用层:代码审计、SCA
- 容器层:配置加固、RASP
- 系统层:内存完整性校验、行为监控
八、总结对比
| 组件类型 | 核心注入点 | 隐蔽性关键 | 主要威胁方式 | 检测难点 |
|---|---|---|---|---|
| Servlet | service()/doGet()/doPost() | 特定URL路径 | 通过特定URL路径执行指令 | 恶意路径枚举 |
| Filter | doFilter() | 监听所有请求(/*) | 拦截所有请求匹配暗号 | 无特定路径,需深度分析链 |
| Listener | requestInitialized(), contextInitialized() | 无URL映射,监听事件 | 作为加载器、非回显操作 | 非常隐蔽,事件监听难察觉 |
防御Java内存马需要采取纵深防御策略,结合漏洞修补、运行时监控、行为分析和安全基线的综合手段。随着云原生技术的发展,内存马攻防将向更底层转移,要求安全团队掌握系统级防护能力。