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;
}
}
四、漏洞复现步骤
- 正常访问路由并输入测试数据
- 发送构造的特殊请求:
- 设置
Content-Length比实际内容长度大1
- 设置
- 再次发送请求观察响应:
- 可以看到上次请求的数据被泄露
五、漏洞深入分析
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时:
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内部请求处理机制,特别是缓冲区管理和请求解析流程,对于防御此类攻击至关重要。