JSP型内存马的原理学习与实现
字数 2088 2025-08-10 13:48:29

JSP型内存马原理与实现详解

前言

JSP型内存马是一种基于Tomcat容器的持久化后门技术,它通过操纵JSP的加载和执行流程来实现隐蔽驻留。本文将系统性地分析JSP型内存马的实现原理和技术细节。

JSP执行流程概述

理解JSP型内存马的前提是掌握Tomcat下JSP的执行过程:

  1. 用户请求一个JSP页面
  2. JSP容器将JSP页面转换为Servlet源码
  3. 将转换后的Java源码编译成.class字节码
  4. 加载编译后的.class并执行逻辑
  5. 将执行结果返回给用户端

JSP加载流程深入分析

1. 请求处理入口

请求首先经过一系列Filter,然后传递给JspServlet#service方法处理:

  • 获取JSP请求的URL信息和调试模式日志打印
  • 通过preCompile方法判断是否对请求资源进行预编译
  • PRECOMPILE常量是jsp_precompile,只有在请求JSP页面带有jsp_precompile查询参数时才会预编译

2. JSP处理核心方法

调用serviceJspFile方法处理JSP请求:

  1. 调用JspRuntimeContext#getWrapper获取请求JSP对应的wrapper

    • 首次访问返回null,会创建新的JspServletWrapper对象
    • 将新创建的wrapper添加到JspServletWrapper类的jsps属性中
    • 会先检查JSP资源是否存在,不存在则抛出异常
  2. JspServletWrapper是Servlet的包装类,所有注册的JSP Servlet都存放在rctxt

3. JSP编译过程

JspServletWrapper#service方法中的关键编译环节:

  • 在满足this.options.getDevelopment() || this.mustCompile条件时进入编译环节
  • 默认使用org.apache.jasper.compiler.JDTCompiler编译器
  • 通过isOutDated方法判断是否需要重新编译
  • 删除已存在的.java和.class文件(首次访问时不存在)
  • 调用JDTCompiler#compile进行编译:
    • generateJava方法生成请求资源的Java代码
    • generateClass方法编译Java代码
  • 编译完成后将mustCompile属性置为false

4. JSP Servlet注册

调用JspServletWrapper#getServlet方法进行JSP Servlet注册:

  1. 判断this.getReloadInternal() || this.theServlet == null

    • theServlet用于判断Servlet是否存在(首次访问为null)
    • destroy方法用于销毁Servlet
  2. 创建Servlet实例:

InstanceManager instanceManager = InstanceManagerFactory.getInstanceManager(this.config);
servlet = (Servlet)instanceManager.newInstance(this.ctxt.getFQCN(), this.ctxt.getJspLoader());
  1. 初始化JSP Servlet
  2. 将创建好的Servlet保存在theServlet属性中
  3. 最后调用servlet#service方法处理请求

JSP型内存马实现原理

要实现一个有效的JSP型内存马,必须满足以下条件:

  1. 持久化能力:能够在服务器重启后仍然存在
  2. 隐蔽性:不依赖磁盘上的JSP文件,避免被常规扫描发现
  3. 动态加载:能够动态注册并执行恶意代码
  4. 访问触发:通过正常请求路径触发执行

关键技术点

  1. 动态注册JSP Servlet

    • 通过操纵JspRuntimeContext注册恶意Servlet
    • 绕过正常的JSP文件检查机制
  2. 内存驻留

    • 将恶意代码直接注入到内存中的Servlet实例
    • 避免生成物理JSP文件
  3. 请求路由

    • 将特定路径映射到恶意Servlet
    • 保持与正常JSP请求相同的处理流程

实现步骤

  1. 获取JSP运行时环境

    • 通过反射获取JspRuntimeContext实例
    • 获取当前应用的Servlet上下文
  2. 创建恶意Servlet包装类

    • 继承或模拟JspServletWrapper行为
    • 实现自定义的编译和加载逻辑
  3. 注册恶意Servlet

    • 将恶意wrapper添加到jsps集合中
    • 确保能够通过正常URL访问
  4. 实现恶意功能

    • 在Servlet的service方法中植入后门逻辑
    • 支持命令执行、文件操作等功能

防御措施

  1. 运行时监控

    • 监控异常的Servlet注册行为
    • 检查JspRuntimeContext中的可疑wrapper
  2. 静态分析

    • 扫描内存中的Servlet实例
    • 检查是否有未对应物理JSP文件的Servlet
  3. 安全加固

    • 限制JSP编译功能
    • 禁用不必要的Servlet API
  4. 行为检测

    • 监控异常的JSP请求处理流程
    • 检测绕过正常编译流程的行为

总结

JSP型内存马通过深入理解Tomcat的JSP处理机制,操纵JSP的编译和Servlet注册过程,实现了在内存中驻留恶意代码的能力。防御此类攻击需要从静态检测和动态监控两方面入手,重点关注JSP处理流程中的异常行为。

JSP型内存马原理与实现详解 前言 JSP型内存马是一种基于Tomcat容器的持久化后门技术,它通过操纵JSP的加载和执行流程来实现隐蔽驻留。本文将系统性地分析JSP型内存马的实现原理和技术细节。 JSP执行流程概述 理解JSP型内存马的前提是掌握Tomcat下JSP的执行过程: 用户请求一个JSP页面 JSP容器将JSP页面转换为Servlet源码 将转换后的Java源码编译成.class字节码 加载编译后的.class并执行逻辑 将执行结果返回给用户端 JSP加载流程深入分析 1. 请求处理入口 请求首先经过一系列Filter,然后传递给 JspServlet#service 方法处理: 获取JSP请求的URL信息和调试模式日志打印 通过 preCompile 方法判断是否对请求资源进行预编译 PRECOMPILE 常量是 jsp_precompile ,只有在请求JSP页面带有 jsp_precompile 查询参数时才会预编译 2. JSP处理核心方法 调用 serviceJspFile 方法处理JSP请求: 调用 JspRuntimeContext#getWrapper 获取请求JSP对应的wrapper 首次访问返回null,会创建新的 JspServletWrapper 对象 将新创建的wrapper添加到 JspServletWrapper 类的 jsps 属性中 会先检查JSP资源是否存在,不存在则抛出异常 JspServletWrapper 是Servlet的包装类,所有注册的JSP Servlet都存放在 rctxt 中 3. JSP编译过程 JspServletWrapper#service 方法中的关键编译环节: 在满足 this.options.getDevelopment() || this.mustCompile 条件时进入编译环节 默认使用 org.apache.jasper.compiler.JDTCompiler 编译器 通过 isOutDated 方法判断是否需要重新编译 删除已存在的.java和.class文件(首次访问时不存在) 调用 JDTCompiler#compile 进行编译: generateJava 方法生成请求资源的Java代码 generateClass 方法编译Java代码 编译完成后将 mustCompile 属性置为false 4. JSP Servlet注册 调用 JspServletWrapper#getServlet 方法进行JSP Servlet注册: 判断 this.getReloadInternal() || this.theServlet == null theServlet 用于判断Servlet是否存在(首次访问为null) destroy 方法用于销毁Servlet 创建Servlet实例: 初始化JSP Servlet 将创建好的Servlet保存在 theServlet 属性中 最后调用 servlet#service 方法处理请求 JSP型内存马实现原理 要实现一个有效的JSP型内存马,必须满足以下条件: 持久化能力 :能够在服务器重启后仍然存在 隐蔽性 :不依赖磁盘上的JSP文件,避免被常规扫描发现 动态加载 :能够动态注册并执行恶意代码 访问触发 :通过正常请求路径触发执行 关键技术点 动态注册JSP Servlet : 通过操纵 JspRuntimeContext 注册恶意Servlet 绕过正常的JSP文件检查机制 内存驻留 : 将恶意代码直接注入到内存中的Servlet实例 避免生成物理JSP文件 请求路由 : 将特定路径映射到恶意Servlet 保持与正常JSP请求相同的处理流程 实现步骤 获取JSP运行时环境 : 通过反射获取 JspRuntimeContext 实例 获取当前应用的Servlet上下文 创建恶意Servlet包装类 : 继承或模拟 JspServletWrapper 行为 实现自定义的编译和加载逻辑 注册恶意Servlet : 将恶意wrapper添加到 jsps 集合中 确保能够通过正常URL访问 实现恶意功能 : 在Servlet的 service 方法中植入后门逻辑 支持命令执行、文件操作等功能 防御措施 运行时监控 : 监控异常的Servlet注册行为 检查 JspRuntimeContext 中的可疑wrapper 静态分析 : 扫描内存中的Servlet实例 检查是否有未对应物理JSP文件的Servlet 安全加固 : 限制JSP编译功能 禁用不必要的Servlet API 行为检测 : 监控异常的JSP请求处理流程 检测绕过正常编译流程的行为 总结 JSP型内存马通过深入理解Tomcat的JSP处理机制,操纵JSP的编译和Servlet注册过程,实现了在内存中驻留恶意代码的能力。防御此类攻击需要从静态检测和动态监控两方面入手,重点关注JSP处理流程中的异常行为。