CVE-2024-21733 tomcat请求走私分析
字数 1404 2025-08-22 22:47:39

CVE-2024-21733 Tomcat请求走私漏洞深入分析与教学文档

一、漏洞概述

CVE-2024-21733是一个影响Apache Tomcat的HTTP请求走私漏洞,主要影响Tomcat 9.0.43版本。该漏洞允许攻击者通过精心构造的HTTP请求,利用前后端服务器对请求边界解析不一致的特性,实现请求走私攻击。

二、请求走私原理

1. 基本概念

HTTP请求走私发生在以下场景:

  • Web应用程序使用HTTP服务器链(前端服务器+后端服务器)
  • 前端服务器通过同一网络连接发送多个请求到后端服务器
  • 前后端系统对请求边界解析不一致

2. 攻击原理

攻击者发送一个精心构造的模糊请求:

  • 前端服务器以一种方式解析请求边界
  • 后端服务器以另一种方式解析请求边界
  • 导致前端请求的一部分被后端解释为下一个请求的开始

三、环境搭建

1. 测试环境配置

  • Spring框架搭建
  • 内置Tomcat版本:9.0.43
  • 测试路由示例:
public class tomcat {
    @RequestMapping({"/cve"})
    public String cve(HttpServletRequest request) {
        String name = request.getParameter("name");
        return "Your name is:" + name;
    }
}

四、漏洞复现步骤

  1. 正常访问路由并输入测试数据
  2. 发送构造的特殊请求:
    • 设置Content-Length比实际内容长度大1
  3. 再次发送请求观察响应:
    • 可以看到上次请求的数据被泄露

五、漏洞深入分析

1. 正常请求处理流程

关键调用链:

parseParameters:3216, Request (org.apache.catalina.connector)
getParameter:1142, Request (org.apache.catalina.connector)
getParameter:381, RequestFacade (org.apache.catalina.connector)
cve:20, tomcat (com.example.headerstealer.controller)

2. 关键方法分析

parseParameters方法

int len = getContentLength();
byte[] formData = null;
if (len < CACHED_POST_LEN) {
    if (postData == null) {
        postData = new byte[CACHED_POST_LEN];
    }
    formData = postData;
} else {
    formData = new byte[len];
}

try {
    if (readPostBody(formData, len) != len) {
        parameters.setParseFailedReason(FailReason.REQUEST_BODY_INCOMPLETE);
        return;
    }
}

readPostBody方法

protected int readPostBody(byte[] body, int len) throws IOException {
    int offset = 0;
    do {
        int inputLen = getStream().read(body, offset, len - offset);
        if (inputLen <= 0) {
            return offset;
        }
        offset += inputLen;
    } while ((len - offset) > 0);
    return len;
}

3. 请求处理结束后的清理

service方法中的关键部分:

if (!isAsync() || getErrorState().isError()) {
    request.updateCounters();
    if (getErrorState().isIoAllowed()) {
        inputBuffer.nextRequest();
        outputBuffer.nextRequest();
    }
}

nextRequest方法

void nextRequest() {
    request.recycle();
    if (byteBuffer.position() > 0) {
        if (byteBuffer.remaining() > 0) {
            // Copy leftover bytes to the beginning of the buffer
            byteBuffer.compact();
            byteBuffer.flip();
        } else {
            // Reset position and limit to 0
            byteBuffer.position(0).limit(0);
        }
    }
    // ...其他清理代码...
}

4. parseRequestLine方法分析

关键的第二阶段处理:

if (parsingRequestLinePhase == 2) {
    boolean space = false;
    while (!space) {
        if (byteBuffer.position() >= byteBuffer.limit()) {
            if (!fill(false)) return false;
        }
        int pos = byteBuffer.position();
        chr = byteBuffer.get();
        if (chr == Constants.SP || chr == Constants.HT) {
            space = true;
            request.method().setBytes(byteBuffer.array(), parsingRequestLineStart, pos - parsingRequestLineStart);
        } else if (!HttpParser.isToken(chr)) {
            request.protocol().setString(Constants.HTTP_11);
            String invalidMethodValue = parseInvalid(parsingRequestLineStart, byteBuffer);
            throw new IllegalArgumentException(sm.getString("iib.invalidmethod", invalidMethodValue));
        }
    }
    parsingRequestLinePhase = 3;
}

5. 漏洞触发原因

数据泄露机制

当数据没有正常的token时会抛出异常:

if (!HttpParser.isToken(chr)) {
    request.protocol().setString(Constants.HTTP_11);
    String invalidMethodValue = parseInvalid(parsingRequestLineStart, byteBuffer);
    throw new IllegalArgumentException(sm.getString("iib.invalidmethod", invalidMethodValue));
}

其中invalidMethodValue就是泄露的请求数据。

Content-Length的影响

关键计算公式:

len(leakage) = len(previous body) + len(actually length) + 1 - len(Content-Length)

Content-Length设置比实际长度大1时:

  1. readPostBody方法会尝试读取比实际数据多1字节
  2. 导致缓冲区处理异常
  3. 最终在parseRequestLine阶段泄露数据

六、技术细节问题解答

Q: 为什么在parseRequestLine方法时byteBuffer的值会变成不合理的值?

A: 这是因为当Content-Length设置不正确时:

  1. readPostBody方法读取的数据不完整
  2. 缓冲区(byteBuffer)中残留了部分数据
  3. nextRequest方法中,这些残留数据没有被正确清理
  4. 当处理下一个请求时,这些残留数据被错误地解析为请求的一部分

Q: 具体赋值如何根据content长度计算?

A: 赋值过程如下:

  1. 首先根据Content-Length分配缓冲区大小
  2. 尝试读取指定长度的数据
  3. 如果实际数据不足:
    • 缓冲区中会保留部分旧数据
    • 这些旧数据会被compact()操作移动到缓冲区开头
  4. 当下一个请求到来时,这些残留数据会被错误解析

七、防护建议

  1. 升级Tomcat到最新版本
  2. 在前端代理服务器上严格验证HTTP请求
  3. 禁用不安全的HTTP方法
  4. 实施严格的请求大小限制
  5. 监控和记录异常的HTTP请求模式

八、总结

CVE-2024-21733漏洞展示了HTTP请求走私攻击的潜在危害,通过精心构造的Content-Length值,攻击者可以泄露敏感数据。理解Tomcat内部请求处理机制,特别是缓冲区管理和请求解析流程,对于防御此类攻击至关重要。

CVE-2024-21733 Tomcat请求走私漏洞深入分析与教学文档 一、漏洞概述 CVE-2024-21733是一个影响Apache Tomcat的HTTP请求走私漏洞,主要影响Tomcat 9.0.43版本。该漏洞允许攻击者通过精心构造的HTTP请求,利用前后端服务器对请求边界解析不一致的特性,实现请求走私攻击。 二、请求走私原理 1. 基本概念 HTTP请求走私发生在以下场景: Web应用程序使用HTTP服务器链(前端服务器+后端服务器) 前端服务器通过同一网络连接发送多个请求到后端服务器 前后端系统对请求边界解析不一致 2. 攻击原理 攻击者发送一个精心构造的模糊请求: 前端服务器以一种方式解析请求边界 后端服务器以另一种方式解析请求边界 导致前端请求的一部分被后端解释为下一个请求的开始 三、环境搭建 1. 测试环境配置 Spring框架搭建 内置Tomcat版本:9.0.43 测试路由示例: 四、漏洞复现步骤 正常访问路由并输入测试数据 发送构造的特殊请求: 设置 Content-Length 比实际内容长度大1 再次发送请求观察响应: 可以看到上次请求的数据被泄露 五、漏洞深入分析 1. 正常请求处理流程 关键调用链: 2. 关键方法分析 parseParameters方法 readPostBody方法 3. 请求处理结束后的清理 service 方法中的关键部分: nextRequest方法 4. parseRequestLine方法分析 关键的第二阶段处理: 5. 漏洞触发原因 数据泄露机制 当数据没有正常的token时会抛出异常: 其中 invalidMethodValue 就是泄露的请求数据。 Content-Length的影响 关键计算公式: 当 Content-Length 设置比实际长度大1时: readPostBody 方法会尝试读取比实际数据多1字节 导致缓冲区处理异常 最终在 parseRequestLine 阶段泄露数据 六、技术细节问题解答 Q: 为什么在parseRequestLine方法时byteBuffer的值会变成不合理的值? A: 这是因为当 Content-Length 设置不正确时: readPostBody 方法读取的数据不完整 缓冲区( byteBuffer )中残留了部分数据 在 nextRequest 方法中,这些残留数据没有被正确清理 当处理下一个请求时,这些残留数据被错误地解析为请求的一部分 Q: 具体赋值如何根据content长度计算? A: 赋值过程如下: 首先根据 Content-Length 分配缓冲区大小 尝试读取指定长度的数据 如果实际数据不足: 缓冲区中会保留部分旧数据 这些旧数据会被 compact() 操作移动到缓冲区开头 当下一个请求到来时,这些残留数据会被错误解析 七、防护建议 升级Tomcat到最新版本 在前端代理服务器上严格验证HTTP请求 禁用不安全的HTTP方法 实施严格的请求大小限制 监控和记录异常的HTTP请求模式 八、总结 CVE-2024-21733漏洞展示了HTTP请求走私攻击的潜在危害,通过精心构造的 Content-Length 值,攻击者可以泄露敏感数据。理解Tomcat内部请求处理机制,特别是缓冲区管理和请求解析流程,对于防御此类攻击至关重要。